Loaded required libraries
| knitr |
| FactoMineR |
| factoextra |
| gprofiler2 |
| pheatmap |
| biomaRt |
Synopsis du projet
Travail demandé
Le but de ce travail est de mettre en oeuvre les méthodes vues dans le module 3 “R et statistiques” pour explorer le jeu de données de Pavkovic, et de rendre un rapport d’analyse au format .Rmd.
Nous fournissons ci-dessous une trame avec les principales sections attendues. Certaines contiennent déjà du code. Vous devrez en compléter d’autres. Sentez-vous libres d’adapter cette trame ou d’y ajouter des analyses complémentaires si elles vous aident à interpréter vos résultats.
Remise du rapport
Date: le 17 mai 2021 minuit. Si vous anticipez un problème pour remettre le rapport à cette date contactez-nous aussi rapidement que possible pour que nous puissions prévoir une remise plus tardive.
- Commencez par renommer le fichier .Rmd en remplaçant Prenom-NOM par vos nom et prénom.
- Le rapport est attendu en formats .Rmd + .HTML (en gardant l’option self_contained de l’en-tête activée).
Déposez les fichiers dans un sous-dossier de vote compte du cluster. Attention, veillez à respecter précisément cette structure de chemin car nous nous baserons dessus pour récupérer vos résultats.
/shared/projects/dubii2021/[login]/m3-stat-R/mini-projet
Critères d’évaluation
- Reproductibilité des analyses: nous tenterons de regénérer le rapport HTML à partir de votre Rmd, en partant de notre compte sur le serveur IFB.
- Manipulation des objets R
- Mobilisation des méthodes statistiques vues au cours
- Pertinence des interprétations statistiques
- Pertinence des interprétations biologiques
- Clarté de la rédaction
- Clarté des illustrations (figures et tableaux): graphismes, légendes …
Nous vous encourageons à assurer la lisibilité de votre code (syntaxe, nommage des variables, commentaires de code)
Objectifs scientifiques
Nous partons du même jeu de données Fil Rouge de ce module issues de la publication Pavkovic, M., Pantano, L., Gerlach, C.V. et al. Multi omics analysis of fibrotic kidneys in two mouse models. Sci Data 6, 92 (2019). https://doi.org/10.1038/s41597-019-0095-5
Rappel sur les échantillons:
Deux modèles de fibrose rénale chez la souris sont étudiés:
Le premier est un modèle de néphropathie réversible induite par l’acide folique (folic acid (FA)). Les souris ont été sacrifiées avant le traitement (normal), puis à jour 1, 2, 7 et 14 (day1,…) après une seule injection d’acide folique.
Le second est un modèle irréversible induit chrirurgicalement (unilateral ureteral obstruction (UUO)). les souris ont été sacrifiées avant obstruction (day 0) et à 3, 7 et 14 jours après obstruction par ligation de l’uretère du rein gauche.
A partir de ces extraits de rein, l’ARN messager total et les petits ARNs ont été séquencés et les protéines caratérisées par spectrométrie de masse en tandem (TMT).
But scientifique: Dans le tutoriel sur les dataframes, vous avez travaillé sur les données de transcriptome du modèle UUO. Dans ce mini-projet, vous allez travailler sur les données du transcriptome du modèle FA afin de regrouper les observations (échantillon) et les gènes selon des profils d’expression similaires.
Votre projet se décompose en 5 parties dont 3 seront à réaliser par:
- Statitiques descriptives des données brutes: commandes fournies
- Normalisation des données : commandes fournies
- Statistiques descriptives des données normalisées: à vous de jouer
- Analyse de regroupement des données: à vous de jouer
- Analyse d’enrichissement fonctionnel: à vous de jouer
1. Les données brutes
Vous n’avez rien à coder ici. Le code est fourni.
Chargement des données brutes
Le bloc suivant contient une fonction qui permet de télécharger un fichier dans l’espace de travail, sauf s’il est déjà présent. Nous l’utiliserons ensuite pour télécharger les données à analyser en évitant de refaire le transfert à chaque exécution de l’analyse.
#' @title Download a file only if it is not yet here
#' @author Jacques van Helden email{Jacques.van-Helden@@france-bioinformatique.fr}
#' @param url_base base of the URL, that will be prepended to the file name
#' @param file_name name of the file (should not contain any path)
#' @param local_folder path of a local folder where the file should be stored
#' @return the function returns the path of the local file, built from local_folder and file_name
#' @export©
download_only_once <- function(
url_base,
file_name,
local_folder) {
## Define the source URL
url <- file.path(url_base, file_name)
message("Source URL\n\t", url)
## Define the local file
local_file <- file.path(local_folder, file_name)
## Create the local data folder if it does not exist
dir.create(local_folder, showWarnings = FALSE, recursive = TRUE)
## Download the file ONLY if it is not already there
if (!file.exists(local_file)) {
message("Downloading file from source URL to local file\n\t",
local_file)
download.file(url = url, destfile = local_file)
} else {
message("Local file already exists, no need to download\n\t",
local_file)
}
return(local_file)
}
Nous téléchargeons deux fichiers dans un dossier local ~/m3-stat-R/pavkovic_analysis (vous pouvez changer le nom ou chemin dans le chunk ci-dessous), et les chargeons dans les data.frames suivants:
- Données brutes de transcriptome:
fa_expr_raw
- Métadonnées:
fa_meta
## Define the remote URL and local folder
pavkovic_url <- "https://github.com/DU-Bii/module-3-Stat-R/raw/master/stat-R_2021/data/pavkovic_2019/"
## Define the local folder for this analysis (where the data will be downloaded and the results generated)
pavkovic_folder <- "~/m3-stat-R/pavkovic_analysis"
## Define a sub-folder for the data
pavkovic_data_folder <- file.path(pavkovic_folder, "data")
## Download and load the expression data table
## Note: we use check.names=FALSE to avoid replacing hyphens by dots
## in sample names, because we want to keep them as in the
## original data files.
message("Downloading FA transcriptome file\t", "fa_raw_counts.tsv.gz",
"\n\tfrom\t", pavkovic_url)
fa_expr_file <- download_only_once(
url_base = pavkovic_url,
file_name = "fa_raw_counts.tsv.gz",
local_folder = pavkovic_data_folder)
## Load the expresdsion table
message("Loading FA transcriptome data from\n\t", fa_expr_file)
fa_expr_raw <- read.delim(file = fa_expr_file,
header = TRUE,
row.names = 1)
## Download the metadata file
message("Downloading FA metadata file\t", "fa_transcriptome_metadata.tsv",
"\n\tfrom\t", pavkovic_url)
fa_meta_file <- download_only_once(
url_base = pavkovic_url,
file_name = "fa_transcriptome_metadata.tsv",
local_folder = pavkovic_data_folder)
## Load the metadata
message("Loading FA metadata from\n\t", fa_meta_file)
fa_meta <- read.delim(file = fa_meta_file,
header = TRUE,
row.names = 1)
Nous regardons la structure de chaque dataframe.
'data.frame': 46679 obs. of 18 variables:
$ day1_1 : num 2278.8 0 36.3 13.2 0 ...
$ day1_2 : num 1786.5 0 22.15 7.15 27.9 ...
$ day1_3 : num 2368.62 0 39.48 1.12 6.9 ...
$ day14_1 : num 627.758 0 14.471 0.867 5.692 ...
$ day14_2 : num 559.2 0 10.2 0 1.9 ...
$ day14_3 : num 611.434 0 31.691 0 0.655 ...
$ day2_1 : num 2145.22 0 300.56 1.71 57.38 ...
$ day2_2 : num 262.45 0 4.77 0 0 ...
$ day2_3 : num 745.84 0 123.9 5.26 38.9 ...
$ day3_1 : num 987.185 0 51.856 0.802 8.931 ...
$ day3_2 : num 1077.65 0 8.43 0 6.97 ...
$ day3_3 : num 1335.1 0 69.9 0 0 ...
$ day7_1 : num 1096.08 0 6.67 0 7.94 ...
$ day7_2 : num 1035.846 0 6.955 0.849 101.648 ...
$ day7_3 : num 1090.04 0 42.58 1.71 0.65 ...
$ normal_1: num 483.23 0 7.35 0.86 32.06 ...
$ normal_2: num 1842.1 0 11.2 0 10.4 ...
$ normal_3: num 475.7 0 1.03 0 0 ...
'data.frame': 18 obs. of 5 variables:
$ dataType : chr "transcriptome" "transcriptome" "transcriptome" "transcriptome" ...
$ sampleName : chr "day14_1" "day14_2" "day14_3" "day1_1" ...
$ condition : chr "day14" "day14" "day14" "day1" ...
$ sampleNumber: int 1 2 3 1 2 3 1 2 3 1 ...
$ color : chr "#FF4400" "#FF4400" "#FF4400" "#BBD7FF" ...
Les deux fichiers ne donnent pas les observations de l’échantillon dans le même ordre:
[1] FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
Nous les réorganisons les échantillons dans l’ordre de l’expérience: condition normale, puis day 1 à 14 avec les 3 réplicats.
sample_order <- c(paste(rep(c("normal", "day1", "day2", "day3", "day7", "day14"), each = 3),
1:3, sep = "_"))
fa_expr_raw <- fa_expr_raw[,sample_order]
fa_meta <- fa_meta[match(sample_order, fa_meta$sampleName),]
# View(fa_meta)
kable(fa_meta, caption = "Metdata for Pavkovoc FA transcriptome")
Metdata for Pavkovoc FA transcriptome
| 16 |
transcriptome |
normal_1 |
normal |
1 |
#BBFFBB |
| 17 |
transcriptome |
normal_2 |
normal |
2 |
#BBFFBB |
| 18 |
transcriptome |
normal_3 |
normal |
3 |
#BBFFBB |
| 4 |
transcriptome |
day1_1 |
day1 |
1 |
#BBD7FF |
| 5 |
transcriptome |
day1_2 |
day1 |
2 |
#BBD7FF |
| 6 |
transcriptome |
day1_3 |
day1 |
3 |
#BBD7FF |
| 7 |
transcriptome |
day2_1 |
day2 |
1 |
#F0BBFF |
| 8 |
transcriptome |
day2_2 |
day2 |
2 |
#F0BBFF |
| 9 |
transcriptome |
day2_3 |
day2 |
3 |
#F0BBFF |
| 10 |
transcriptome |
day3_1 |
day3 |
1 |
#FFFFDD |
| 11 |
transcriptome |
day3_2 |
day3 |
2 |
#FFFFDD |
| 12 |
transcriptome |
day3_3 |
day3 |
3 |
#FFFFDD |
| 13 |
transcriptome |
day7_1 |
day7 |
1 |
#FFDD88 |
| 14 |
transcriptome |
day7_2 |
day7 |
2 |
#FFDD88 |
| 15 |
transcriptome |
day7_3 |
day7 |
3 |
#FFDD88 |
| 1 |
transcriptome |
day14_1 |
day14 |
1 |
#FF4400 |
| 2 |
transcriptome |
day14_2 |
day14 |
2 |
#FF4400 |
| 3 |
transcriptome |
day14_3 |
day14 |
3 |
#FF4400 |
=> Ainsi, nous avons un jeu de données avec un échantillon de 18 observations et des données d’expression de 46679 gènes.
Statistiques descriptives
Dans le tutorial sur les dataframes sur le jeu de données “uuo” (relisez le corrigé), nous vous avons demandé de créer un data.frame qui collecte les statistiques par gène et par échantillon. Nous vous demandons de réaliser une étude similaire sur les données “FA” avant et après normalisation inter-échantillons des données. Le code de la partie avant normalisation est donné.
Par échantillon avant normalisation
Nous créons un data.frame nommé sample_stat_prenorm qui comporte une ligne par échantillon et une colonne par statistique. Nous calculons les statistiques suivantes sur les valeurs log2 d’expression de chaque échantillon:
- moyenne
- écart-type
- intervalle inter-quartiles
- premier quartile
- médiane
- troisième quartile
- maximum
- nombre de valeurs nulles
Il est affiché avec la fonction kable().
message("Computing sample-wise statistics on raw counts")
sample_stat_prenorm <- data.frame(
mean = apply(fa_expr_raw, 2, mean, na.rm = TRUE),
sd = apply(fa_expr_raw, 2, sd, na.rm = TRUE),
iqr = apply(fa_expr_raw, 2, IQR, na.rm = TRUE),
Q1 = apply(fa_expr_raw, 2, quantile, p = 0.25, na.rm = TRUE),
median = apply(fa_expr_raw, 2, median, na.rm = TRUE),
Q3 = apply(fa_expr_raw, 2, quantile, p = 0.75, na.rm = TRUE),
max = apply(fa_expr_raw, 2, max, na.rm = TRUE),
null = apply(fa_expr_raw == 0, 2, sum, na.rm = TRUE)
)
kable(sample_stat_prenorm, caption = "Sample-wise statistics before normalisation.")
Sample-wise statistics before normalisation.
| normal_1 |
576.2992 |
57643.81 |
46.38417 |
0 |
1.166694 |
46.38417 |
11777395 |
21415 |
| normal_2 |
1172.5615 |
111509.82 |
103.13749 |
0 |
2.975840 |
103.13749 |
22658521 |
20203 |
| normal_3 |
531.8620 |
57203.96 |
41.80724 |
0 |
1.081680 |
41.80724 |
11636735 |
21660 |
| day1_1 |
663.0580 |
65219.36 |
58.76393 |
0 |
1.105540 |
58.76393 |
13174979 |
21700 |
| day1_2 |
1224.5149 |
123140.72 |
111.71166 |
0 |
3.053561 |
111.71166 |
25118595 |
20152 |
| day1_3 |
1368.6144 |
133450.04 |
122.38187 |
0 |
3.766666 |
122.38187 |
27255572 |
19621 |
| day2_1 |
1161.7628 |
103598.87 |
127.04744 |
0 |
3.501480 |
127.04744 |
21036079 |
19978 |
| day2_2 |
223.8844 |
20431.97 |
14.54242 |
0 |
0.000000 |
14.54242 |
4037963 |
24446 |
| day2_3 |
628.4406 |
57480.66 |
63.36112 |
0 |
1.478198 |
63.36112 |
11662570 |
21455 |
| day3_1 |
649.8389 |
55210.88 |
77.84371 |
0 |
2.076066 |
77.84371 |
11170233 |
20772 |
| day3_2 |
514.8649 |
42768.34 |
74.16479 |
0 |
1.941705 |
74.16479 |
8612320 |
21142 |
| day3_3 |
854.0997 |
80565.29 |
104.95124 |
0 |
2.975772 |
104.95124 |
16484449 |
20287 |
| day7_1 |
576.2437 |
53112.82 |
90.51017 |
0 |
2.256164 |
90.51017 |
10690493 |
20865 |
| day7_2 |
510.2741 |
47373.18 |
80.57478 |
0 |
2.537673 |
80.57478 |
9682527 |
20467 |
| day7_3 |
604.9701 |
59072.56 |
83.50019 |
0 |
2.874706 |
83.50019 |
12164986 |
20045 |
| day14_1 |
540.2640 |
54481.09 |
65.01270 |
0 |
2.025707 |
65.01270 |
11178198 |
20496 |
| day14_2 |
681.9987 |
74825.35 |
69.05744 |
0 |
2.032894 |
69.05744 |
15430799 |
20870 |
| day14_3 |
562.5697 |
56026.62 |
75.11681 |
0 |
2.247271 |
75.11681 |
11575718 |
20396 |
Par gène avant normalisation
Nous créons ci-dessous un data.frame nommé gene_stat_prenorm qui comporte une ligne par gène et une colonne par statistique. Nous calculons les statistiques suivantes sur les valeurs log2 de chaque gène.
- moyenne
- médiane
- écart-type
- premier quartile
- troisième quartile
- maximum
- nombre de valeurs nulles
- intervalle inter-quartiles
Ces résultats sont stockés dans un data.frame avec 1 ligne par échantillon et 1 colonne par statistique. Nous affichons les lignes 100 à 109 de ce tableau de statistiques avec la fonction kable().
## Gene-wise statistics for the raw counts (will be used for normalisation)
message("Computing gene-wise statistics on raw counts")
gene_stat_prenorm <- data.frame(
mean = apply(fa_expr_raw, 1, mean, na.rm = TRUE),
sd = apply(fa_expr_raw, 1, sd, na.rm = TRUE),
iqr = apply(fa_expr_raw, 1, IQR, na.rm = TRUE),
Q1 = apply(fa_expr_raw, 1, quantile, p = 0.25, na.rm = TRUE),
median = apply(fa_expr_raw, 1, median, na.rm = TRUE),
Q3 = apply(fa_expr_raw, 1, quantile, p = 0.75, na.rm = TRUE),
max = apply(fa_expr_raw, 1, max, na.rm = TRUE),
null = apply(fa_expr_raw == 0, 1, sum, na.rm = TRUE)
)
kable(gene_stat_prenorm[100:109, ], caption = "Gene-wise statistics before normalisation")
Gene-wise statistics before normalisation
| ENSMUSG00000000567 |
320.336744 |
286.399084 |
364.362298 |
91.046357 |
261.659973 |
455.40866 |
1052.68945 |
0 |
| ENSMUSG00000000568 |
857.348825 |
694.432189 |
282.978949 |
575.571199 |
711.096105 |
858.55015 |
3406.45030 |
0 |
| ENSMUSG00000000579 |
422.075702 |
162.290415 |
157.732606 |
340.417017 |
447.572742 |
498.14962 |
728.51737 |
0 |
| ENSMUSG00000000581 |
498.978227 |
248.612029 |
259.324338 |
320.599085 |
471.269224 |
579.92342 |
942.51104 |
0 |
| ENSMUSG00000000594 |
2113.558334 |
1237.521694 |
937.571105 |
1400.226182 |
1868.718762 |
2337.79729 |
6408.96044 |
0 |
| ENSMUSG00000000600 |
2299.978163 |
911.222891 |
1480.192582 |
1719.874980 |
2093.572929 |
3200.06756 |
3674.18811 |
0 |
| ENSMUSG00000000605 |
325.552118 |
169.456624 |
195.879561 |
221.735904 |
282.608205 |
417.61546 |
791.31256 |
0 |
| ENSMUSG00000000606 |
9.929258 |
32.151350 |
2.736779 |
1.178311 |
1.934744 |
3.91509 |
138.48677 |
4 |
| ENSMUSG00000000617 |
18.804116 |
9.470819 |
13.654701 |
12.188276 |
18.150391 |
25.84298 |
36.00235 |
0 |
| ENSMUSG00000000627 |
15.623306 |
14.341852 |
12.308127 |
4.780913 |
10.064895 |
17.08904 |
46.29752 |
0 |
2. Filtrage et normalisation des données
Vous n’avez rien à coder ici. Le code est fourni.
Il existe plusieurs façons de normaliser les données de transcriptome vues dans les modules 4 et 5 (cf. total counts, quantiles, TMM, RLE, limma voom,…), mais nous avons choisi ici une solution simple tout en étant robuste pour normaliser les données en standardisant le 3ème quantile.
La méthode choisie ici consiste à :
Ecarter les gènes “non-détectés”, c’est-à-dire ceux ayant des valeurs nulles dans au moins 90% des échantillons.
Ecarter les gènes à peine exprimés, c’est-à-dire ceux ayant une valeur moyenne < 10 (arbitrairement).
Standardiser les échantillons sur le 3ème quartile des gènes restants: on divise les comptages bruts par le 3ème quartile de l’échantillon et on multiplie par le 3ème quartile de l’ensemble des échantillons.
Normaliser les comptages (au sens propre, c’est-à-dire rapprocher leur distribution de la distribution gaussienne) par une transformation logarithmique (log2).
Nous fournissons ci-dessous le code.
Filtrage : élimination des gènes non détectés ou à peine exprimés
[1] "Undetected genes (null in >= 90% samples): 14380"
[1] "Barely expressed genes (mean < 10): 26286"
[1] "Discarded genes: 26288"
[1] "Kept genes: 20391"
Standardisation entre échantillons
Nous appliquons ici une méthode simple mais efficace de standardisation
Third quartile of the filtered counts after inter-sample standardisation of the third quartiles.
| normal_1 |
364.9332 |
| normal_2 |
364.9332 |
| normal_3 |
364.9332 |
| day1_1 |
364.9332 |
| day1_2 |
364.9332 |
| day1_3 |
364.9332 |
| day2_1 |
364.9332 |
| day2_2 |
364.9332 |
| day2_3 |
364.9332 |
| day3_1 |
364.9332 |
| day3_2 |
364.9332 |
| day3_3 |
364.9332 |
| day7_1 |
364.9332 |
| day7_2 |
364.9332 |
| day7_3 |
364.9332 |
| day14_1 |
364.9332 |
| day14_2 |
364.9332 |
| day14_3 |
364.9332 |
3. Statistiques descriptives sur les données normalisées
A vous de jouer!
Statistiques par gène après normalisation
Générez un data.frame avec une ligne par gène à partir du tableau de données normalisées, avec les statistiques suivantes (une statistique par colonne):
- moyenne
- variance
- écart-type
- coefficient de variation (écart-type divisé par la moyenne)
- intervalle inter-quartiles
- minimum
- médiane
- maximum
## Gene-wise statistics after normalisation
message("Computing gene-wise statistics on log2-transformed and normalised counts")
gene_stat_norm <- data.frame(
mean = apply(fa_expr_log2, 1, mean, na.rm = F),
var = apply(fa_expr_log2, 1, var, na.rm = TRUE),
sd = apply(fa_expr_log2, 1, sd, na.rm = F),
CV = NA,
min = apply(fa_expr_log2, 1, min, na.rm = TRUE),
Q1 = apply(fa_expr_log2, 1, quantile, p = 0.25, na.rm = TRUE),
median = apply(fa_expr_log2, 1, median, na.rm = TRUE),
Q3 = apply(fa_expr_log2, 1, quantile, p = 0.75, na.rm = TRUE),
max = apply(fa_expr_log2, 1, max, na.rm = TRUE),
null = apply(fa_expr_log2 == 0, 1, sum, na.rm = TRUE)
)
gene_stat_norm$CV <- gene_stat_norm$sd / gene_stat_norm$mean
Annotation des gènes
Chaque gène étant donné par son identifiant dans la base de données ENSEMBL vous utiliserez le paquet biomaRt de bioconductor pour ajouter des annotations : symbole, chromosome, coordonnées génomiques, brin. Suivez pas à pas la méthode proposée (certaines étapes peuvent prendre quelques minutes):
chargez le paquet biomaRt, voire installer-le uniquement si nécessaire. Indiquez le code à l’emplacement adéquat dans de .Rmd.
sélectionnez la base de données ENSEMBL avec la fonction useMart(). Attention à choisir le bon génome avec l’agument dataset: mmusculus_gene_ensembl
avec la fonction getBM() récupérez de la base de données ENSEMBL les champs demandés (pour symbole utilisez external_gene_name) en appliquant “ensembl_geneid” pour l’agument filter et en indiquant pour l’argument values le vecteur des identifiants des gènes présents dans le dataframe gene_stat_norm. Vous obtenez un dataframe.
A présent, ajoutez au dataframe gene_stat_norm en 1ères colonnes les annotations retrouvées grâce à biomaRt. Attention, certains gènes ne sont pas retrouvés dans la version d’ENSEMBL sur biomaRt donc laissez des NA comme données manquantes dans ce cas. Nous vous recommandons d’utiliser la function merge() de R base ou bien left_join() de dplyr pour fusionner les deux dataframes en un seul.
### Gene annotations ####
## Open a connection to Ensembl MART
message("Opening connection to Ensembl MART")
ensembl <- useMart("ENSEMBL_MART_ENSEMBL",
host = "www.ensembl.org",
dataset = "mmusculus_gene_ensembl")
## Get gene annotations
message("Getting gene annotations")
genes <- getBM(attributes = c("ensembl_gene_id",
"external_gene_name",
"chromosome_name",
"start_position",
"end_position",
"strand"),
filter = "ensembl_gene_id",
values = row.names(gene_stat_norm),
mart = ensembl)
## Merge gene annotations and expression statistics
gene_stat_norm <- merge(genes, gene_stat_norm, by.x = "ensembl_gene_id", by.y = 0, sort = FALSE)
kable(gene_stat_norm[100:109, ], caption = "Gene-wise statistics after normalisation")
Challenge falcultatif:
Réordonnez les gènes par position génomique et affichez les lignes 5 premières et 5 dernières lignes de ce tableau de statistiques.
Distribution des données
- Dessinez sous forme d’un histogramme la distribution des valeurs après normalisation (tous échantillons confondus)
hist(unlist(fa_expr_log2),
breaks = seq(from = 0, to = max(fa_expr_log2) + 1, by = 0.25),
xlab = "log2(counts) after standardisation",
ylab = "number of genes after filtering",
col = "#BBDDFF",
las = 1, cex.axis = 0.8,
main = "distribution after standardisation")
abline(v = mean(fa_expr_log2), col = "darkgreen", lwd = 2)
- Dessinez un box plot par échantillon avant et après normalisation, et commentez la façon dont l’effet de la normalisation apparaît sur ces graphiques.
#### Box plots to show normalisation impact ####
par(mar = c(4,6,4,1)) ## Set the margins
par(mfrow = c(2,2))
boxplot(fa_expr_raw,
horizontal = TRUE,
xlab = "counts",
las = 1,
col = fa_meta$color,
main = "Raw counts, all genes")
boxplot(fa_expr_filtered,
horizontal = TRUE,
xlab = "counts",
las = 1,
col = fa_meta$color,
main = "Raw counts, filtered genes")
boxplot(fa_expr_standard,
horizontal = TRUE,
xlab = "counts",
las = 1,
col = fa_meta$color,
main = "Standardised counts (sample Q3)")
boxplot(fa_expr_log2,
xlab = "log2(counts)",
las = 1,
horizontal = TRUE,
col = fa_meta$color,
main = "Normalised counts")
4. Analyse de regroupement des données
A vous de jouer!
Sélection de gènes d’expression élevée et variable
Pour réduire le nombre de gènes, nous allons écarter les gènes faiblement exprimés (log2 moyen inférieur à 4), et ne retenir que ceux qui montrent des variations importantes entre échantillons. Pour ce dernier critère, nous nous basons sur la variance.
Sélectionnez les gènes ayant un niveau log2 moyen minimal supérieur à 5 (\(m > 5\)) et une variance supérieure à 2 (\(s^2 > 2\)). Note: ces valeurs sont parfaitement arbitraires, elles ont été choisies pour obtenir un nombre raisonnable de gènes.
[1] "Selected genes: 690"
Dessinez des histogrammes des valeurs d’expression avant et après cette sélection de gènes, et commentez les différences.
#### Histograms of expression before and after gene selection ####
par(mfrow = c(2,1))
hist(unlist(fa_expr_log2),
breaks = seq(from = 0, to = max(fa_expr_log2) + 1, by = 0.25),
las = 1,
cex.axis = 0.8,
main = "Standardized values before gene selection",
col = "#DDBBFF")
hist(unlist(fa_expr_selected),
breaks = seq(from = 0, to = max(fa_expr_log2) + 1, by = 0.25),
las = 1,
cex.axis = 0.8,
main = "Standardized values after gene selection",
col = "#FFDDBB")
Dessinez un box plot par échantillon des valeurs d’expression avant et après sélection des gènes, et commentez le résultat.
#### Histogram of expression after gene selection ####
par(mfrow = c(1,2))
boxplot(fa_expr_log2,
horizontal = TRUE,
xlab = "log2(counts)",
las = 1,
col = fa_meta$color,
main = "Before gene selection")
boxplot(fa_expr_selected,
horizontal = TRUE,
xlab = "log2(counts)",
las = 1,
col = fa_meta$color,
main = "After gene selection")
ACP
Dessinez un plot ACP des échantillons en les colorant par condition avant et après normalisation.
- avec les comptages bruts de la matrice d’expression initiale (\(fa_expr\))
- avec la matrice de valeurs normalisées des gènes filtrés
- avec la matrice finale (transformation log2, filtre des gènes non-détectés, standardisation et sélection des gènes fortement exprimés et à haut coefficient de variation)
Clustering
- Calculez les matrices de distance entre échantillons, en utilisant respectivement les distances euclidienne (
dist()), coefficient de Pearson (cor(, method = "pearson")) et de Spearman (cor(, method = "spearman")).
- Effectuez un clustering hiérarchique des échantillons, en utilisant le critère “complete” pour l’agglomération. Comparez les arbres d’échantillons obtenus avec ces trois métriques et choisissez celle qui vous paraît la plus pertinente.
- Effectuez un clustering hiérarchique des gènes en utilisant la distance basée sur le coefficient de Pearson et le critère de Ward

- Dessinez un arbre avec le résultat du clustering des gènes et commentez sa structure. Si vous deviez choisir de façon arbitraire un nombre de clusters, que choisiriez-vous ? Pourquoi ? Pas de panique, nous pouvons assumer ici que la réponse comporte une part de subjectivité.

- Dessinez une heatmap du résultat, en sélectionnant les deux résultats de clustering ci-dessus pour les gènes et les échantillons.
- Dessinez une heatmap du résultat, en affichant un arbre sur les gènes mais pas sur les échantillons

Interprétez les résultats en quelques phrases.
5. Enrichissement fonctionnel
A vous de jouer!
Effectuez une analyse d’enrichissement fonctionnel avec les principaux clusters obtenus dans la section précédente.
#### Run enrichment analysis with gost() ####
message("Running enrichment analysis with gost")
## Get the list of IDs for the selected genes ##
gene_ids <- rownames(gene_stat_norm)[selected_genes]
## Get the list of gene names for the selected genes ##
# gene_names <- unlist(subset(x = gene_stat_norm,
# subset = selected_genes,
# select = external_gene_name))
# length(gene_names)
##
gostres <- gost(query = gene_ids,
organism = "mmusculus",
ordered_query = FALSE,
multi_query = FALSE,
significant = TRUE,
exclude_iea = FALSE,
measure_underrepresentation = FALSE,
evcodes = FALSE,
user_threshold = 0.05,
correction_method = "fdr",
domain_scope = "annotated",
custom_bg = NULL,
numeric_ns = "",
sources = NULL,
as_short_link = FALSE)
## Check the structure of the result
names(gostres)
[1] "result" "meta"
[1] "query" "significant" "p_value" "term_size" "query_size" "intersection_size" "precision" "recall" "term_id" "source" "term_name" "effective_domain_size"
[13] "source_order" "parents"
| result |
14 |
data.frame |
list |
| meta |
5 |
-none- |
list |
# hist(gostres$result$p_value, breaks=seq(from=0, to=1, by = 0.05))
## Check the most significant results, formating for kable
#head(gostres$result)
enrich_order <- order(gostres$result$p_value, decreasing = FALSE)
sorted_result <- gostres$result[enrich_order, ]
kable(head(sorted_result, n = 10),
digits = c(0, 0, 15, 7, 7, 7, 3, 3, 0, 0, 7, 7, 0, 0),
caption = "Top 10 most significant enriched functional classes")
Top 10 most significant enriched functional classes
| 60 |
query_1 |
TRUE |
0.000000000682655 |
92 |
266 |
20 |
0.075 |
0.217 |
KEGG:04610 |
KEGG |
Complement and coagulation cascades |
8787 |
299 |
KEGG:00000 |
| 1 |
query_1 |
TRUE |
0.000000039452735 |
272 |
320 |
31 |
0.097 |
0.114 |
GO:0006955 |
GO:BP |
immune response |
11240 |
2901 |
GO:0002376, GO:0050896 |
| 2 |
query_1 |
TRUE |
0.000000161876615 |
331 |
320 |
33 |
0.103 |
0.100 |
GO:0002376 |
GO:BP |
immune system process |
11240 |
1038 |
GO:0008150 |
| 61 |
query_1 |
TRUE |
0.000000212705027 |
116 |
266 |
19 |
0.071 |
0.164 |
KEGG:05150 |
KEGG |
Staphylococcus aureus infection |
8787 |
416 |
KEGG:00000 |
| 26 |
query_1 |
TRUE |
0.000006518770910 |
627 |
204 |
42 |
0.206 |
0.067 |
GO:0071944 |
GO:CC |
cell periphery |
7218 |
3147 |
GO:0110165 |
| 27 |
query_1 |
TRUE |
0.000006518770910 |
10 |
204 |
6 |
0.029 |
0.600 |
GO:0042613 |
GO:CC |
MHC class II protein complex |
7218 |
2067 |
GO:0042611 |
| 28 |
query_1 |
TRUE |
0.000009336222258 |
11 |
204 |
6 |
0.029 |
0.545 |
GO:0042611 |
GO:CC |
MHC protein complex |
7218 |
2065 |
GO:0098797 |
| 62 |
query_1 |
TRUE |
0.000017723712072 |
113 |
266 |
16 |
0.060 |
0.142 |
KEGG:04668 |
KEGG |
TNF signaling pathway |
8787 |
321 |
KEGG:00000 |
| 63 |
query_1 |
TRUE |
0.000017723712072 |
86 |
266 |
14 |
0.053 |
0.163 |
KEGG:05323 |
KEGG |
Rheumatoid arthritis |
8787 |
460 |
KEGG:00000 |
| 125 |
query_1 |
TRUE |
0.000029996303079 |
41 |
174 |
11 |
0.063 |
0.268 |
WP:WP3626 |
WP |
Microglia Pathogen Phagocytosis Pathway |
4526 |
46 |
WP:000000 |
# kable(head(gostres$result),
# format = c("%s", "%s", "%e", "%d", "%d", "%d", "%.3f", "%.3f", "%s", "%s", "%d", "%d", "%s", "%s"))
names(gostres$meta)
[1] "query_metadata" "result_metadata" "genes_metadata" "timestamp" "version"
Conclusions générales
Résumez en quelques phrases vos conclusions à partir des résultats obtenus.
Session info
R version 4.0.2 (2020-06-22)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Mojave 10.14.6
Matrix products: default
BLAS: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRblas.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] biomaRt_2.44.4 pheatmap_1.0.12 gprofiler2_0.2.0 factoextra_1.0.7 ggplot2_3.3.3 FactoMineR_2.4 knitr_1.32
loaded via a namespace (and not attached):
[1] bitops_1.0-6 bit64_4.0.5 RColorBrewer_1.1-2 progress_1.2.2 httr_1.4.2 tools_4.0.2 backports_1.2.1 bslib_0.2.4 utf8_1.2.1 R6_2.5.0 DT_0.18 DBI_1.1.1 lazyeval_0.2.2 BiocGenerics_0.34.0
[15] colorspace_2.0-0 withr_2.4.2 tidyselect_1.1.0 prettyunits_1.1.1 bit_4.0.4 curl_4.3 compiler_4.0.2 Biobase_2.48.0 flashClust_1.01-2 xml2_1.3.2 plotly_4.9.3 labeling_0.4.2 sass_0.3.1 scales_1.1.1
[29] askpass_1.1 rappdirs_0.3.3 stringr_1.4.0 digest_0.6.27 foreign_0.8-81 rmarkdown_2.7 rio_0.5.26 pkgconfig_2.0.3 htmltools_0.5.1.1 dbplyr_2.1.1 fastmap_1.1.0 highr_0.9 readxl_1.3.1 htmlwidgets_1.5.3
[43] rlang_0.4.10 RSQLite_2.2.6 shiny_1.6.0 farver_2.1.0 jquerylib_0.1.3 generics_0.1.0 jsonlite_1.7.2 crosstalk_1.1.1 zip_2.1.1 dplyr_1.0.5 car_3.0-10 RCurl_1.98-1.3 magrittr_2.0.1 leaps_3.1
[57] Rcpp_1.0.6 munsell_0.5.0 S4Vectors_0.26.1 fansi_0.4.2 abind_1.4-5 lifecycle_1.0.0 scatterplot3d_0.3-41 stringi_1.5.3 yaml_2.2.1 carData_3.0-4 MASS_7.3-53.1 BiocFileCache_1.12.1 grid_4.0.2 blob_1.2.1
[71] promises_1.2.0.1 parallel_4.0.2 ggrepel_0.9.1 forcats_0.5.1 crayon_1.4.1 lattice_0.20-41 haven_2.4.0 hms_1.0.0 pillar_1.6.0 ggpubr_0.4.0 ggsignif_0.6.1 stats4_4.0.2 XML_3.99-0.6 glue_1.4.2
[85] evaluate_0.14 data.table_1.14.0 httpuv_1.5.5 vctrs_0.3.7 cellranger_1.1.0 gtable_0.3.0 openssl_1.4.3 purrr_0.3.4 tidyr_1.1.3 assertthat_0.2.1 cachem_1.0.4 openxlsx_4.2.3 xfun_0.22 mime_0.10
[99] xtable_1.8-4 broom_0.7.6 later_1.1.0.1 rstatix_0.7.0 viridisLite_0.4.0 tibble_3.1.1 AnnotationDbi_1.50.3 memoise_2.0.0 IRanges_2.22.2 cluster_2.1.2 ellipsis_0.3.1
LS0tCnRpdGxlOiAiTWluaS1wcm9qZXQgMjAyMSAtIEV4cGxvcmF0aW9uIGRlcyBkb25uw6llcyBkZSBQYXZrb3ZpYyIKYXV0aG9yOiAiUHLDqW5vbSBOb20iCmRhdGU6ICdgciBTeXMuRGF0ZSgpYCcKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBzZWxmX2NvbnRhaW5lZDogeWVzCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgIHRoZW1lOiBjZXJ1bGVhbgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogMwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNvZGVfZm9sZGluZzogImhpZGUiCiAgcGRmX2RvY3VtZW50OgogICAgZmlnX2NhcHRpb246IHllcwogICAgaGlnaGxpZ2h0OiB6ZW5idXJuCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQotLS0KCgpgYGB7ciBzZXR0aW5ncywgaW5jbHVkZT1GQUxTRSwgZWNobz1GQUxTRSwgZXZhbD1UUlVFfQpvcHRpb25zKHdpZHRoID0gMzAwKQojIG9wdGlvbnMoZW5jb2RpbmcgPSAnVVRGLTgnKQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDUsIAogIGZpZy5wYXRoID0gJ2ZpZ3VyZXMvbWluaS1wcm9qZXRfJywKICBmaWcuYWxpZ24gPSAiY2VudGVyIiwgCiAgc2l6ZSA9ICJ0aW55IiwgCiAgZWNobyA9IFRSVUUsIAogIGV2YWwgPSBUUlVFLCAKICB3YXJuaW5nID0gRkFMU0UsIAogIG1lc3NhZ2UgPSBGQUxTRSwgCiAgcmVzdWx0cyA9IFRSVUUsIAogIGNvbW1lbnQgPSAiIikKCm9wdGlvbnMoc2NpcGVuID0gMTIpICMjIE1heCBudW1iZXIgb2YgZGlnaXRzIGZvciBub24tc2NpZW50aWZpYyBub3RhdGlvbgojIGtuaXRyOjphc2lzX291dHB1dCgiXFxmb290bm90ZXNpemUiKQpgYGAKCmBgYHtyIGxpYnJhcmllcywgZWNobz1GQUxTRSwgZXZhbD1UUlVFfQojIyMjIFJlcXVpcmVkIGxpYnJhcmllcyAjIyMjCgojIExvYWQgcmVxdWlyZWQgQ1JBTiBSIGxpYnJhcmllcwpyZXF1aXJlZF9jcmFuTGliIDwtIGMoImtuaXRyIiwgCiAgICAgICAgICAgICAgICAgICAgICAiRmFjdG9NaW5lUiIsIAogICAgICAgICAgICAgICAgICAgICAgImZhY3RvZXh0cmEiLCAKICAgICAgICAgICAgICAgICAgICAgICJncHJvZmlsZXIyIiwKICAgICAgICAgICAgICAgICAgICAgICJwaGVhdG1hcCIpCmZvciAobGliIGluIHJlcXVpcmVkX2NyYW5MaWIpIHsKICBpZiAoIXJlcXVpcmUobGliLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7CiAgICBpbnN0YWxsLnBhY2thZ2VzKGxpYikKICB9CiAgcmVxdWlyZShsaWIsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkKfQoKcmVxdWlyZWRfYmlvY0xpYiA8LSBjKCJiaW9tYVJ0IikKZm9yIChsaWIgaW4gcmVxdWlyZWRfYmlvY0xpYikgewogIGlmICghcmVxdWlyZShsaWIsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIHsKICAgIEJpb2NNYW5hZ2VyOjppbnN0YWxsKGxpYikKICB9CiAgcmVxdWlyZShsaWIsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkKfQoKa2FibGUoYXMuZGF0YS5mcmFtZShjKHJlcXVpcmVkX2NyYW5MaWIsIHJlcXVpcmVkX2Jpb2NMaWIpKSwKICAgICAgY29sLm5hbWVzID0gImxpYnJhcmllcyIsCiAgICAgIGNhcHRpb24gPSAiTG9hZGVkIHJlcXVpcmVkIGxpYnJhcmllcyIKICAgICkKCmBgYAoKIyMgU3lub3BzaXMgZHUgcHJvamV0CgojIyMgVHJhdmFpbCBkZW1hbmTDqQoKTGUgYnV0IGRlIGNlIHRyYXZhaWwgZXN0IGRlIG1ldHRyZSBlbiBvZXV2cmUgbGVzIG3DqXRob2RlcyB2dWVzIGRhbnMgbGUgbW9kdWxlIDMgIlIgZXQgc3RhdGlzdGlxdWVzIiBwb3VyIGV4cGxvcmVyIGxlIGpldSBkZSBkb25uw6llcyBkZSBQYXZrb3ZpYywgZXQgZGUgcmVuZHJlIHVuIHJhcHBvcnQgZCdhbmFseXNlIGF1IGZvcm1hdCBgLlJtZGAuIAoKTm91cyBmb3Vybmlzc29ucyBjaS1kZXNzb3VzIHVuZSB0cmFtZSBhdmVjIGxlcyBwcmluY2lwYWxlcyBzZWN0aW9ucyBhdHRlbmR1ZXMuIENlcnRhaW5lcyBjb250aWVubmVudCBkw6lqw6AgZHUgY29kZS4gVm91cyBkZXZyZXogZW4gY29tcGzDqXRlciBkJ2F1dHJlcy4gU2VudGV6LXZvdXMgbGlicmVzIGQnYWRhcHRlciBjZXR0ZSB0cmFtZSBvdSBkJ3kgYWpvdXRlciBkZXMgYW5hbHlzZXMgY29tcGzDqW1lbnRhaXJlcyBzaSBlbGxlcyB2b3VzIGFpZGVudCDDoCBpbnRlcnByw6l0ZXIgdm9zIHLDqXN1bHRhdHMuIAoKIyMjIFJlbWlzZSBkdSByYXBwb3J0CgpEYXRlOiAqKmxlIDE3IG1haSAyMDIxIG1pbnVpdCoqLiAgU2kgdm91cyBhbnRpY2lwZXogdW4gcHJvYmzDqG1lIHBvdXIgcmVtZXR0cmUgbGUgcmFwcG9ydCDDoCBjZXR0ZSBkYXRlIGNvbnRhY3Rlei1ub3VzIGF1c3NpIHJhcGlkZW1lbnQgcXVlIHBvc3NpYmxlIHBvdXIgcXVlIG5vdXMgcHVpc3Npb25zIHByw6l2b2lyIHVuZSByZW1pc2UgcGx1cyB0YXJkaXZlLiAKCi0gQ29tbWVuY2V6IHBhciByZW5vbW1lciBsZSBmaWNoaWVyIC5SbWQgZW4gcmVtcGxhw6dhbnQgUHJlbm9tLU5PTSBwYXIgdm9zIG5vbSBldCBwcsOpbm9tLiAKLSBMZSByYXBwb3J0IGVzdCBhdHRlbmR1IGVuIGZvcm1hdHMgLlJtZCArIC5IVE1MIChlbiBnYXJkYW50IGwnb3B0aW9uIHNlbGZfY29udGFpbmVkIGRlIGwnZW4tdMOqdGUgYWN0aXbDqWUpLiAKLSBEw6lwb3NleiBsZXMgZmljaGllcnMgZGFucyB1biBzb3VzLWRvc3NpZXIgZGUgdm90ZSBjb21wdGUgZHUgY2x1c3Rlci4gQXR0ZW50aW9uLCB2ZWlsbGV6IMOgIHJlc3BlY3RlciBwcsOpY2lzw6ltZW50IGNldHRlIHN0cnVjdHVyZSBkZSBjaGVtaW4gY2FyIG5vdXMgbm91cyBiYXNlcm9ucyBkZXNzdXMgcG91ciByw6ljdXDDqXJlciB2b3MgcsOpc3VsdGF0cy4gCgogICAgYC9zaGFyZWQvcHJvamVjdHMvZHViaWkyMDIxL1tsb2dpbl0vbTMtc3RhdC1SL21pbmktcHJvamV0YCAKCiMjIyBDcml0w6hyZXMgZCfDqXZhbHVhdGlvbgoKLSBSZXByb2R1Y3RpYmlsaXTDqSBkZXMgYW5hbHlzZXM6IG5vdXMgdGVudGVyb25zIGRlIHJlZ8OpbsOpcmVyIGxlIHJhcHBvcnQgSFRNTCDDoCBwYXJ0aXIgZGUgdm90cmUgUm1kLCBlbiBwYXJ0YW50IGRlIG5vdHJlIGNvbXB0ZSBzdXIgbGUgc2VydmV1ciBJRkIuIAotIE1hbmlwdWxhdGlvbiBkZXMgb2JqZXRzIFIKLSBNb2JpbGlzYXRpb24gZGVzIG3DqXRob2RlcyBzdGF0aXN0aXF1ZXMgdnVlcyBhdSBjb3VycwotIFBlcnRpbmVuY2UgZGVzIGludGVycHLDqXRhdGlvbnMgc3RhdGlzdGlxdWVzCi0gUGVydGluZW5jZSBkZXMgaW50ZXJwcsOpdGF0aW9ucyBiaW9sb2dpcXVlcwotIENsYXJ0w6kgZGUgbGEgcsOpZGFjdGlvbgotIENsYXJ0w6kgZGVzIGlsbHVzdHJhdGlvbnMgKGZpZ3VyZXMgZXQgdGFibGVhdXgpOiBncmFwaGlzbWVzLCBsw6lnZW5kZXMgLi4uCgpOb3VzIHZvdXMgZW5jb3VyYWdlb25zIMOgIGFzc3VyZXIgbGEgbGlzaWJpbGl0w6kgZGUgdm90cmUgY29kZSAoc3ludGF4ZSwgbm9tbWFnZSBkZXMgdmFyaWFibGVzLCBjb21tZW50YWlyZXMgZGUgY29kZSkKCiMjIyBPYmplY3RpZnMgc2NpZW50aWZpcXVlcwoKTm91cyBwYXJ0b25zIGR1IG3Dqm1lIGpldSBkZSBkb25uw6llcyAqRmlsIFJvdWdlKiBkZSBjZSBtb2R1bGUgaXNzdWVzIGRlIGxhIHB1YmxpY2F0aW9uIFBhdmtvdmljLCBNLiwgUGFudGFubywgTC4sIEdlcmxhY2gsIEMuVi4gZXQgYWwuIE11bHRpIG9taWNzIGFuYWx5c2lzIG9mIGZpYnJvdGljIGtpZG5leXMgaW4gdHdvIG1vdXNlIG1vZGVscy4gU2NpIERhdGEgNiwgOTIgKDIwMTkpLiBodHRwczovL2RvaS5vcmcvMTAuMTAzOC9zNDE1OTctMDE5LTAwOTUtNQoKKipSYXBwZWwgc3VyIGxlcyDDqWNoYW50aWxsb25zOioqCgpEZXV4IG1vZMOobGVzIGRlIGZpYnJvc2UgcsOpbmFsZSBjaGV6IGxhIHNvdXJpcyBzb250IMOpdHVkacOpczoKCjEuIExlIHByZW1pZXIgZXN0IHVuIG1vZMOobGUgZGUgbsOpcGhyb3BhdGhpZSByw6l2ZXJzaWJsZSBpbmR1aXRlIHBhciBsJ2FjaWRlIGZvbGlxdWUgKGZvbGljIGFjaWQgKEZBKSkuIExlcyBzb3VyaXMgb250IMOpdMOpIHNhY3JpZmnDqWVzIGF2YW50IGxlIHRyYWl0ZW1lbnQgKG5vcm1hbCksIHB1aXMgw6Agam91ciAxLCAyLCA3IGV0IDE0IChkYXkxLC4uLikgYXByw6hzIHVuZSBzZXVsZSBpbmplY3Rpb24gZCdhY2lkZSBmb2xpcXVlLgoKMi4gTGUgc2Vjb25kIGVzdCB1biBtb2TDqGxlIGlycsOpdmVyc2libGUgaW5kdWl0IGNocmlydXJnaWNhbGVtZW50ICh1bmlsYXRlcmFsIHVyZXRlcmFsIG9ic3RydWN0aW9uIChVVU8pKS4gbGVzIHNvdXJpcyBvbnQgw6l0w6kgc2FjcmlmacOpZXMgYXZhbnQgb2JzdHJ1Y3Rpb24gKGRheSAwKSBldCDDoCAzLCA3IGV0IDE0IGpvdXJzIGFwcsOocyBvYnN0cnVjdGlvbiBwYXIgbGlnYXRpb24gZGUgbCd1cmV0w6hyZSBkdSByZWluIGdhdWNoZS4KCkEgcGFydGlyIGRlIGNlcyBleHRyYWl0cyBkZSByZWluLCBsJ0FSTiBtZXNzYWdlciB0b3RhbCBldCBsZXMgcGV0aXRzIEFSTnMgb250IMOpdMOpIHPDqXF1ZW5jw6lzIGV0IGxlcyBwcm90w6lpbmVzIGNhcmF0w6lyaXPDqWVzIHBhciBzcGVjdHJvbcOpdHJpZSBkZSBtYXNzZSBlbiB0YW5kZW0gKFRNVCkuCgoqKkJ1dCBzY2llbnRpZmlxdWU6KiogRGFucyBsZSB0dXRvcmllbCBzdXIgbGVzIGRhdGFmcmFtZXMsIHZvdXMgYXZleiB0cmF2YWlsbMOpIHN1ciBsZXMgZG9ubsOpZXMgZGUgKioqdHJhbnNjcmlwdG9tZSBkdSBtb2TDqGxlIFVVTyoqKi4gRGFucyBjZSBtaW5pLXByb2pldCwgdm91cyBhbGxleiB0cmF2YWlsbGVyIHN1ciBsZXMgZG9ubsOpZXMgZHUgKioqdHJhbnNjcmlwdG9tZSBkdSBtb2TDqGxlIEZBKioqIGFmaW4gZGUgcmVncm91cGVyIGxlcyBvYnNlcnZhdGlvbnMgKMOpY2hhbnRpbGxvbikgZXQgbGVzIGfDqG5lcyBzZWxvbiBkZXMgcHJvZmlscyBkJ2V4cHJlc3Npb24gc2ltaWxhaXJlcy4KCioqVm90cmUgcHJvamV0IHNlIGTDqWNvbXBvc2UgZW4gNSBwYXJ0aWVzIGRvbnQgMyBzZXJvbnQgw6AgcsOpYWxpc2VyIHBhcjoqKgoKMS4gU3RhdGl0aXF1ZXMgZGVzY3JpcHRpdmVzIGRlcyBkb25uw6llcyBicnV0ZXM6IGNvbW1hbmRlcyBmb3VybmllcwoyLiBOb3JtYWxpc2F0aW9uIGRlcyBkb25uw6llcyA6IGNvbW1hbmRlcyBmb3VybmllcwozLiBTdGF0aXN0aXF1ZXMgZGVzY3JpcHRpdmVzIGRlcyBkb25uw6llcyBub3JtYWxpc8OpZXM6IMOgIHZvdXMgZGUgam91ZXIKNC4gQW5hbHlzZSBkZSByZWdyb3VwZW1lbnQgZGVzIGRvbm7DqWVzOiDDoCB2b3VzIGRlIGpvdWVyCjUuIEFuYWx5c2UgZCdlbnJpY2hpc3NlbWVudCBmb25jdGlvbm5lbDogw6Agdm91cyBkZSBqb3VlcgoKIyMgMS4gTGVzIGRvbm7DqWVzIGJydXRlcwoKKioqVm91cyBuJ2F2ZXogcmllbiDDoCBjb2RlciBpY2kuIExlIGNvZGUgZXN0IGZvdXJuaS4qKioKCiMjIyBDaGFyZ2VtZW50IGRlcyBkb25uw6llcyBicnV0ZXMKCkxlIGJsb2Mgc3VpdmFudCBjb250aWVudCB1bmUgZm9uY3Rpb24gcXVpIHBlcm1ldCBkZSB0w6lsw6ljaGFyZ2VyIHVuIGZpY2hpZXIgZGFucyBsJ2VzcGFjZSBkZSB0cmF2YWlsLCBzYXVmIHMnaWwgZXN0IGTDqWrDoCBwcsOpc2VudC4gTm91cyBsJ3V0aWxpc2Vyb25zIGVuc3VpdGUgcG91ciB0w6lsw6ljaGFyZ2VyIGxlcyBkb25uw6llcyDDoCBhbmFseXNlciBlbiDDqXZpdGFudCBkZSByZWZhaXJlIGxlIHRyYW5zZmVydCDDoCBjaGFxdWUgZXjDqWN1dGlvbiBkZSBsJ2FuYWx5c2UuIAoKYGBge3IgZnVuY3Rpb25fZG93bmxvYWRfb25seV9vbmNlfQojJyBAdGl0bGUgRG93bmxvYWQgYSBmaWxlIG9ubHkgaWYgaXQgaXMgbm90IHlldCBoZXJlCiMnIEBhdXRob3IgSmFjcXVlcyB2YW4gSGVsZGVuIGVtYWlse0phY3F1ZXMudmFuLUhlbGRlbkBAZnJhbmNlLWJpb2luZm9ybWF0aXF1ZS5mcn0KIycgQHBhcmFtIHVybF9iYXNlIGJhc2Ugb2YgdGhlIFVSTCwgdGhhdCB3aWxsIGJlIHByZXBlbmRlZCB0byB0aGUgZmlsZSBuYW1lCiMnIEBwYXJhbSBmaWxlX25hbWUgbmFtZSBvZiB0aGUgZmlsZSAoc2hvdWxkIG5vdCBjb250YWluIGFueSBwYXRoKQojJyBAcGFyYW0gbG9jYWxfZm9sZGVyIHBhdGggb2YgYSBsb2NhbCBmb2xkZXIgd2hlcmUgdGhlIGZpbGUgc2hvdWxkIGJlIHN0b3JlZAojJyBAcmV0dXJuIHRoZSBmdW5jdGlvbiByZXR1cm5zIHRoZSBwYXRoIG9mIHRoZSBsb2NhbCBmaWxlLCBidWlsdCBmcm9tIGxvY2FsX2ZvbGRlciBhbmQgZmlsZV9uYW1lCiMnIEBleHBvcnTCqQpkb3dubG9hZF9vbmx5X29uY2UgPC0gZnVuY3Rpb24oCiAgdXJsX2Jhc2UsIAogIGZpbGVfbmFtZSwKICBsb2NhbF9mb2xkZXIpIHsKCiAgIyMgRGVmaW5lIHRoZSBzb3VyY2UgVVJMICAKICB1cmwgPC0gZmlsZS5wYXRoKHVybF9iYXNlLCBmaWxlX25hbWUpCiAgbWVzc2FnZSgiU291cmNlIFVSTFxuXHQiLCAgdXJsKQoKICAjIyBEZWZpbmUgdGhlIGxvY2FsIGZpbGUKICBsb2NhbF9maWxlIDwtIGZpbGUucGF0aChsb2NhbF9mb2xkZXIsIGZpbGVfbmFtZSkKICAKICAjIyBDcmVhdGUgdGhlIGxvY2FsIGRhdGEgZm9sZGVyIGlmIGl0IGRvZXMgbm90IGV4aXN0CiAgZGlyLmNyZWF0ZShsb2NhbF9mb2xkZXIsIHNob3dXYXJuaW5ncyA9IEZBTFNFLCByZWN1cnNpdmUgPSBUUlVFKQogIAogICMjIERvd25sb2FkIHRoZSBmaWxlIE9OTFkgaWYgaXQgaXMgbm90IGFscmVhZHkgdGhlcmUKICBpZiAoIWZpbGUuZXhpc3RzKGxvY2FsX2ZpbGUpKSB7CiAgICBtZXNzYWdlKCJEb3dubG9hZGluZyBmaWxlIGZyb20gc291cmNlIFVSTCB0byBsb2NhbCBmaWxlXG5cdCIsIAogICAgICAgICAgICBsb2NhbF9maWxlKQogICAgZG93bmxvYWQuZmlsZSh1cmwgPSB1cmwsIGRlc3RmaWxlID0gbG9jYWxfZmlsZSkKICB9IGVsc2UgewogICAgbWVzc2FnZSgiTG9jYWwgZmlsZSBhbHJlYWR5IGV4aXN0cywgbm8gbmVlZCB0byBkb3dubG9hZFxuXHQiLCAKICAgICAgICAgICAgbG9jYWxfZmlsZSkKICB9CiAgCiAgcmV0dXJuKGxvY2FsX2ZpbGUpCn0KYGBgCgpOb3VzIHTDqWzDqWNoYXJnZW9ucyBkZXV4IGZpY2hpZXJzIGRhbnMgdW4gZG9zc2llciBsb2NhbCBgfi9tMy1zdGF0LVIvcGF2a292aWNfYW5hbHlzaXNgICoqKHZvdXMgcG91dmV6IGNoYW5nZXIgbGUgbm9tIG91IGNoZW1pbiBkYW5zIGxlIGNodW5rIGNpLWRlc3NvdXMpKiosIGV0IGxlcyBjaGFyZ2VvbnMgZGFucyBsZXMgZGF0YS5mcmFtZXMgc3VpdmFudHM6IAoKLSBEb25uw6llcyBicnV0ZXMgZGUgdHJhbnNjcmlwdG9tZTogYGZhX2V4cHJfcmF3YAotIE3DqXRhZG9ubsOpZXM6IGBmYV9tZXRhYAoKYGBge3IgZG93bmxvYWRfYW5kX2xvYWR9CiMjIERlZmluZSB0aGUgcmVtb3RlIFVSTCBhbmQgbG9jYWwgZm9sZGVyCnBhdmtvdmljX3VybCA8LSAiaHR0cHM6Ly9naXRodWIuY29tL0RVLUJpaS9tb2R1bGUtMy1TdGF0LVIvcmF3L21hc3Rlci9zdGF0LVJfMjAyMS9kYXRhL3BhdmtvdmljXzIwMTkvIgoKIyMgRGVmaW5lIHRoZSBsb2NhbCBmb2xkZXIgZm9yIHRoaXMgYW5hbHlzaXMgKHdoZXJlIHRoZSBkYXRhIHdpbGwgYmUgZG93bmxvYWRlZCBhbmQgdGhlIHJlc3VsdHMgZ2VuZXJhdGVkKQpwYXZrb3ZpY19mb2xkZXIgPC0gIn4vbTMtc3RhdC1SL3BhdmtvdmljX2FuYWx5c2lzIgoKIyMgRGVmaW5lIGEgc3ViLWZvbGRlciBmb3IgdGhlIGRhdGEKcGF2a292aWNfZGF0YV9mb2xkZXIgPC0gZmlsZS5wYXRoKHBhdmtvdmljX2ZvbGRlciwgImRhdGEiKQoKIyMgRG93bmxvYWQgYW5kIGxvYWQgdGhlIGV4cHJlc3Npb24gZGF0YSB0YWJsZQojIyBOb3RlOiB3ZSB1c2UgY2hlY2submFtZXM9RkFMU0UgdG8gYXZvaWQgcmVwbGFjaW5nIGh5cGhlbnMgYnkgZG90cwojIyBpbiBzYW1wbGUgbmFtZXMsIGJlY2F1c2Ugd2Ugd2FudCB0byBrZWVwIHRoZW0gYXMgaW4gdGhlIAojIyBvcmlnaW5hbCBkYXRhIGZpbGVzLiAKbWVzc2FnZSgiRG93bmxvYWRpbmcgRkEgdHJhbnNjcmlwdG9tZSBmaWxlXHQiLCAiZmFfcmF3X2NvdW50cy50c3YuZ3oiLAogICJcblx0ZnJvbVx0IiwgcGF2a292aWNfdXJsKQpmYV9leHByX2ZpbGUgPC0gZG93bmxvYWRfb25seV9vbmNlKAogIHVybF9iYXNlID0gcGF2a292aWNfdXJsLCAKICBmaWxlX25hbWUgPSAiZmFfcmF3X2NvdW50cy50c3YuZ3oiLAogIGxvY2FsX2ZvbGRlciA9IHBhdmtvdmljX2RhdGFfZm9sZGVyKQoKIyMgTG9hZCB0aGUgZXhwcmVzZHNpb24gdGFibGUKbWVzc2FnZSgiTG9hZGluZyBGQSB0cmFuc2NyaXB0b21lIGRhdGEgZnJvbVxuXHQiLCBmYV9leHByX2ZpbGUpCmZhX2V4cHJfcmF3IDwtIHJlYWQuZGVsaW0oZmlsZSA9IGZhX2V4cHJfZmlsZSwgCiAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSkKCiMjIERvd25sb2FkIHRoZSBtZXRhZGF0YSBmaWxlCm1lc3NhZ2UoIkRvd25sb2FkaW5nIEZBIG1ldGFkYXRhIGZpbGVcdCIsICJmYV90cmFuc2NyaXB0b21lX21ldGFkYXRhLnRzdiIsCiAgIlxuXHRmcm9tXHQiLCBwYXZrb3ZpY191cmwpCmZhX21ldGFfZmlsZSA8LSBkb3dubG9hZF9vbmx5X29uY2UoCiAgdXJsX2Jhc2UgPSBwYXZrb3ZpY191cmwsIAogIGZpbGVfbmFtZSA9ICJmYV90cmFuc2NyaXB0b21lX21ldGFkYXRhLnRzdiIsCiAgbG9jYWxfZm9sZGVyID0gcGF2a292aWNfZGF0YV9mb2xkZXIpCgojIyBMb2FkIHRoZSBtZXRhZGF0YQptZXNzYWdlKCJMb2FkaW5nIEZBIG1ldGFkYXRhIGZyb21cblx0IiwgZmFfbWV0YV9maWxlKQpmYV9tZXRhIDwtIHJlYWQuZGVsaW0oZmlsZSA9IGZhX21ldGFfZmlsZSwgCiAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSkKYGBgCgpOb3VzIHJlZ2FyZG9ucyBsYSBzdHJ1Y3R1cmUgZGUgY2hhcXVlIGRhdGFmcmFtZS4KCmBgYHtyIGluc2VwY3QgZGF0YX0Kc3RyKGZhX2V4cHJfcmF3KQpzdHIoZmFfbWV0YSkKYGBgCgpMZXMgZGV1eCBmaWNoaWVycyBuZSBkb25uZW50IHBhcyBsZXMgb2JzZXJ2YXRpb25zIGRlIGwnw6ljaGFudGlsbG9uIGRhbnMgbGUgbcOqbWUgb3JkcmU6CgpgYGB7ciBjaGVjayBkYXRhIG9yZGVyfQpmYV9tZXRhJHNhbXBsZU5hbWUgPT0gbmFtZXMoZmFfZXhwcl9yYXcpCmBgYAoKTm91cyBsZXMgcsOpb3JnYW5pc29ucyBsZXMgw6ljaGFudGlsbG9ucyBkYW5zIGwnb3JkcmUgZGUgbCdleHDDqXJpZW5jZTogY29uZGl0aW9uIG5vcm1hbGUsIHB1aXMgZGF5IDEgw6AgMTQgYXZlYyBsZXMgMyByw6lwbGljYXRzLgoKYGBge3IgcmVvZGVyIGRhdGF9CnNhbXBsZV9vcmRlciA8LSBjKHBhc3RlKHJlcChjKCJub3JtYWwiLCAiZGF5MSIsICJkYXkyIiwgImRheTMiLCAiZGF5NyIsICJkYXkxNCIpLCBlYWNoID0gMyksCiAgICAgICAgICAgICAgICAgICAgICAgIDE6Mywgc2VwID0gIl8iKSkKCmZhX2V4cHJfcmF3IDwtIGZhX2V4cHJfcmF3WyxzYW1wbGVfb3JkZXJdCmZhX21ldGEgPC0gZmFfbWV0YVttYXRjaChzYW1wbGVfb3JkZXIsIGZhX21ldGEkc2FtcGxlTmFtZSksXQoKIyBWaWV3KGZhX21ldGEpCmthYmxlKGZhX21ldGEsIGNhcHRpb24gPSAiTWV0ZGF0YSBmb3IgUGF2a292b2MgRkEgdHJhbnNjcmlwdG9tZSIpCmBgYAoKPT4gQWluc2ksIG5vdXMgYXZvbnMgdW4gamV1IGRlIGRvbm7DqWVzIGF2ZWMgdW4gw6ljaGFudGlsbG9uIGRlIGByIG5yb3coZmFfbWV0YSlgIG9ic2VydmF0aW9ucyBldCBkZXMgZG9ubsOpZXMgZCdleHByZXNzaW9uIGRlIGByIG5yb3coZmFfZXhwcl9yYXcpYCBnw6huZXMuCgoKIyMjIFN0YXRpc3RpcXVlcyBkZXNjcmlwdGl2ZXMKCkRhbnMgbGUgdHV0b3JpYWwgc3VyIGxlcyBkYXRhZnJhbWVzIHN1ciBsZSBqZXUgZGUgZG9ubsOpZXMgInV1byIgKHJlbGlzZXogbGUgY29ycmlnw6kpLCBub3VzIHZvdXMgYXZvbnMgZGVtYW5kw6kgZGUgY3LDqWVyIHVuIGRhdGEuZnJhbWUgcXVpIGNvbGxlY3RlIGxlcyBzdGF0aXN0aXF1ZXMgcGFyIGfDqG5lIGV0IHBhciDDqWNoYW50aWxsb24uIE5vdXMgdm91cyBkZW1hbmRvbnMgZGUgcsOpYWxpc2VyIHVuZSDDqXR1ZGUgc2ltaWxhaXJlIHN1ciBsZXMgZG9ubsOpZXMgIkZBIiBhdmFudCBldCBhcHLDqHMgbm9ybWFsaXNhdGlvbiBpbnRlci3DqWNoYW50aWxsb25zIGRlcyBkb25uw6llcy4gTGUgY29kZSBkZSBsYSBwYXJ0aWUgYXZhbnQgbm9ybWFsaXNhdGlvbiBlc3QgZG9ubsOpLgoKIyMjIyBQYXIgw6ljaGFudGlsbG9uIGF2YW50IG5vcm1hbGlzYXRpb24KCk5vdXMgY3LDqW9ucyB1biBkYXRhLmZyYW1lIG5vbW3DqSBgc2FtcGxlX3N0YXRfcHJlbm9ybWAgcXVpIGNvbXBvcnRlIHVuZSBsaWduZSBwYXIgw6ljaGFudGlsbG9uIGV0IHVuZSBjb2xvbm5lIHBhciBzdGF0aXN0aXF1ZS4gTm91cyBjYWxjdWxvbnMgbGVzIHN0YXRpc3RpcXVlcyBzdWl2YW50ZXMgc3VyIGxlcyB2YWxldXJzIGxvZzIgZCdleHByZXNzaW9uIGRlIGNoYXF1ZSDDqWNoYW50aWxsb246CgotIG1veWVubmUKLSDDqWNhcnQtdHlwZQotIGludGVydmFsbGUgaW50ZXItcXVhcnRpbGVzCi0gcHJlbWllciBxdWFydGlsZQotIG3DqWRpYW5lCi0gdHJvaXNpw6htZSBxdWFydGlsZQotIG1heGltdW0KLSBub21icmUgZGUgdmFsZXVycyBudWxsZXMKCklsIGVzdCBhZmZpY2jDqSBhdmVjIGxhIGZvbmN0aW9uIGBrYWJsZSgpYC4gCgpgYGB7ciBzYW1wbGVfc3RhdF9wcmVfbm9ybX0KbWVzc2FnZSgiQ29tcHV0aW5nIHNhbXBsZS13aXNlIHN0YXRpc3RpY3Mgb24gcmF3IGNvdW50cyIpCnNhbXBsZV9zdGF0X3ByZW5vcm0gPC0gZGF0YS5mcmFtZSgKICBtZWFuID0gYXBwbHkoZmFfZXhwcl9yYXcsIDIsIG1lYW4sIG5hLnJtID0gVFJVRSksCiAgc2QgPSBhcHBseShmYV9leHByX3JhdywgMiwgc2QsIG5hLnJtID0gVFJVRSksCiAgaXFyID0gYXBwbHkoZmFfZXhwcl9yYXcsIDIsIElRUiwgbmEucm0gPSBUUlVFKSwKICBRMSA9IGFwcGx5KGZhX2V4cHJfcmF3LCAyLCBxdWFudGlsZSwgcCA9IDAuMjUsIG5hLnJtID0gVFJVRSksCiAgbWVkaWFuID0gYXBwbHkoZmFfZXhwcl9yYXcsIDIsIG1lZGlhbiwgbmEucm0gPSBUUlVFKSwKICBRMyA9IGFwcGx5KGZhX2V4cHJfcmF3LCAyLCBxdWFudGlsZSwgcCA9IDAuNzUsIG5hLnJtID0gVFJVRSksCiAgbWF4ID0gYXBwbHkoZmFfZXhwcl9yYXcsIDIsIG1heCwgbmEucm0gPSBUUlVFKSwKICBudWxsID0gYXBwbHkoZmFfZXhwcl9yYXcgPT0gMCwgMiwgc3VtLCBuYS5ybSA9IFRSVUUpCikKCmthYmxlKHNhbXBsZV9zdGF0X3ByZW5vcm0sIGNhcHRpb24gPSAiU2FtcGxlLXdpc2Ugc3RhdGlzdGljcyBiZWZvcmUgbm9ybWFsaXNhdGlvbi4iKQpgYGAKCiMjIyMgUGFyIGfDqG5lIGF2YW50IG5vcm1hbGlzYXRpb24KCk5vdXMgY3LDqW9ucyBjaS1kZXNzb3VzIHVuIGRhdGEuZnJhbWUgbm9tbcOpIGBnZW5lX3N0YXRfcHJlbm9ybWAgcXVpIGNvbXBvcnRlIHVuZSBsaWduZSBwYXIgZ8OobmUgZXQgdW5lIGNvbG9ubmUgcGFyIHN0YXRpc3RpcXVlLiBOb3VzIGNhbGN1bG9ucyBsZXMgc3RhdGlzdGlxdWVzIHN1aXZhbnRlcyBzdXIgbGVzIHZhbGV1cnMgbG9nMiBkZSBjaGFxdWUgZ8OobmUuCgotIG1veWVubmUKLSBtw6lkaWFuZQotIMOpY2FydC10eXBlCi0gcHJlbWllciBxdWFydGlsZQotIHRyb2lzacOobWUgcXVhcnRpbGUKLSBtYXhpbXVtCi0gbm9tYnJlIGRlIHZhbGV1cnMgbnVsbGVzCi0gaW50ZXJ2YWxsZSBpbnRlci1xdWFydGlsZXMKCkNlcyByw6lzdWx0YXRzIHNvbnQgc3RvY2vDqXMgZGFucyB1biBkYXRhLmZyYW1lIGF2ZWMgMSBsaWduZSBwYXIgw6ljaGFudGlsbG9uIGV0IDEgY29sb25uZSBwYXIgc3RhdGlzdGlxdWUuIE5vdXMgYWZmaWNob25zIGxlcyBsaWduZXMgMTAwIMOgIDEwOSBkZSBjZSB0YWJsZWF1IGRlIHN0YXRpc3RpcXVlcyBhdmVjIGxhIGZvbmN0aW9uIGBrYWJsZSgpYC4KCmBgYHtyIGdlbmVfc3RhdF9wcmVfbm9ybX0KIyMgR2VuZS13aXNlIHN0YXRpc3RpY3MgZm9yIHRoZSByYXcgY291bnRzICh3aWxsIGJlIHVzZWQgZm9yIG5vcm1hbGlzYXRpb24pCm1lc3NhZ2UoIkNvbXB1dGluZyBnZW5lLXdpc2Ugc3RhdGlzdGljcyBvbiByYXcgY291bnRzIikKZ2VuZV9zdGF0X3ByZW5vcm0gPC0gZGF0YS5mcmFtZSgKICBtZWFuID0gYXBwbHkoZmFfZXhwcl9yYXcsIDEsIG1lYW4sIG5hLnJtID0gVFJVRSksCiAgc2QgPSBhcHBseShmYV9leHByX3JhdywgMSwgc2QsIG5hLnJtID0gVFJVRSksCiAgaXFyID0gYXBwbHkoZmFfZXhwcl9yYXcsIDEsIElRUiwgbmEucm0gPSBUUlVFKSwKICBRMSA9IGFwcGx5KGZhX2V4cHJfcmF3LCAxLCBxdWFudGlsZSwgcCA9IDAuMjUsIG5hLnJtID0gVFJVRSksCiAgbWVkaWFuID0gYXBwbHkoZmFfZXhwcl9yYXcsIDEsIG1lZGlhbiwgbmEucm0gPSBUUlVFKSwKICBRMyA9IGFwcGx5KGZhX2V4cHJfcmF3LCAxLCBxdWFudGlsZSwgcCA9IDAuNzUsIG5hLnJtID0gVFJVRSksCiAgbWF4ID0gYXBwbHkoZmFfZXhwcl9yYXcsIDEsIG1heCwgbmEucm0gPSBUUlVFKSwKICBudWxsID0gYXBwbHkoZmFfZXhwcl9yYXcgPT0gMCwgMSwgc3VtLCBuYS5ybSA9IFRSVUUpCikKCmthYmxlKGdlbmVfc3RhdF9wcmVub3JtWzEwMDoxMDksIF0sIGNhcHRpb24gPSAiR2VuZS13aXNlIHN0YXRpc3RpY3MgYmVmb3JlIG5vcm1hbGlzYXRpb24iKQpgYGAKCiMjIDIuIEZpbHRyYWdlIGV0IG5vcm1hbGlzYXRpb24gZGVzIGRvbm7DqWVzCgoqKipWb3VzIG4nYXZleiByaWVuIMOgIGNvZGVyIGljaS4gTGUgY29kZSBlc3QgZm91cm5pLioqKgoKSWwgZXhpc3RlIHBsdXNpZXVycyBmYcOnb25zIGRlIG5vcm1hbGlzZXIgbGVzIGRvbm7DqWVzIGRlIHRyYW5zY3JpcHRvbWUgIHZ1ZXMgZGFucyBsZXMgbW9kdWxlcyA0IGV0IDUgKGNmLiB0b3RhbCBjb3VudHMsIHF1YW50aWxlcywgVE1NLCBSTEUsIGxpbW1hIHZvb20sLi4uKSwgbWFpcyBub3VzIGF2b25zIGNob2lzaSBpY2kgdW5lIHNvbHV0aW9uIHNpbXBsZSB0b3V0IGVuIMOpdGFudCByb2J1c3RlIHBvdXIgbm9ybWFsaXNlciBsZXMgZG9ubsOpZXMgZW4gc3RhbmRhcmRpc2FudCBsZSAzw6htZSBxdWFudGlsZS4gCgpMYSBtw6l0aG9kZSBjaG9pc2llIGljaSBjb25zaXN0ZSDDoCA6CgoxLiAqKkVjYXJ0ZXIgbGVzIGfDqG5lcyAibm9uLWTDqXRlY3TDqXMiKiosIGMnZXN0LcOgLWRpcmUgY2V1eCBheWFudCBkZXMgdmFsZXVycyBudWxsZXMgZGFucyBhdSBtb2lucyA5MCUgZGVzIMOpY2hhbnRpbGxvbnMuCgoyLiAqKkVjYXJ0ZXIgbGVzIGfDqG5lcyDDoCBwZWluZSBleHByaW3DqXMqKiwgYydlc3Qtw6AtZGlyZSBjZXV4IGF5YW50IHVuZSB2YWxldXIgbW95ZW5uZSA8IDEwIChhcmJpdHJhaXJlbWVudCkuCgozLiAqKlN0YW5kYXJkaXNlciBsZXMgw6ljaGFudGlsbG9ucyAqKnN1ciBsZSAzw6htZSBxdWFydGlsZSBkZXMgZ8OobmVzIHJlc3RhbnRzOiBvbiBkaXZpc2UgbGVzIGNvbXB0YWdlcyBicnV0cyBwYXIgbGUgM8OobWUgcXVhcnRpbGUgZGUgbCfDqWNoYW50aWxsb24gZXQgb24gbXVsdGlwbGllIHBhciBsZSAzw6htZSBxdWFydGlsZSBkZSBsJ2Vuc2VtYmxlIGRlcyDDqWNoYW50aWxsb25zLgoKNC4gKipOb3JtYWxpc2VyIGxlcyBjb21wdGFnZXMqKiAoYXUgc2VucyBwcm9wcmUsIGMnZXN0LcOgLWRpcmUgcmFwcHJvY2hlciBsZXVyIGRpc3RyaWJ1dGlvbiBkZSBsYSBkaXN0cmlidXRpb24gZ2F1c3NpZW5uZSkgcGFyIHVuZSB0cmFuc2Zvcm1hdGlvbiBsb2dhcml0aG1pcXVlIChsb2cyKS4gCgpOb3VzIGZvdXJuaXNzb25zIGNpLWRlc3NvdXMgbGUgY29kZS4KCiMjIyBGaWx0cmFnZSA6IMOpbGltaW5hdGlvbiBkZXMgZ8OobmVzIG5vbiBkw6l0ZWN0w6lzIG91IMOgIHBlaW5lIGV4cHJpbcOpcwoKYGBge3IgZ2VuZV9maWx0ZXJpbmd9CiMjIERhdGEgZmlsdGVyaW5nOiBnZW5lcyBoYXZpbmcgYXQgbGVhc3QgOTAlIG51bGwgdmFsdWVzCm1lc3NhZ2UoIkZpbHRlcmluZyB1bmRldGVjdGVkIGdlbmVzIikKdW5kZXRlY3RlZF9nZW5lcyA8LSBnZW5lX3N0YXRfcHJlbm9ybSRudWxsID49IG5jb2woZmFfZXhwcl9yYXcpICogMC45CnByaW50KHBhc3RlMCgiVW5kZXRlY3RlZCBnZW5lcyAobnVsbCBpbiA+PSA5MCUgc2FtcGxlcyk6ICIsIHN1bSh1bmRldGVjdGVkX2dlbmVzKSkpCgojIyBEYXRhIGZpbHRlcmluZzogZ2VuZXMgaGF2aW5nIGEgbWVhbiBleHByZXNzaW9uIDwgMTAKbWVzc2FnZSgiRmlsdGVyaW5nIGJhcmVseSBleHByZXNzZWQgZ2VuZXMiKQpiYXJlbHlfZXhwcmVzc2VkX2dlbmVzIDwtIGdlbmVfc3RhdF9wcmVub3JtJG1lYW4gPCAxMApwcmludChwYXN0ZTAoIkJhcmVseSBleHByZXNzZWQgZ2VuZXMgKG1lYW4gPCAxMCk6ICIsIHN1bShiYXJlbHlfZXhwcmVzc2VkX2dlbmVzKSkpCgojIyBBcHBseSBmaWx0ZXJpbmcgb24gYm90aCBjcml0ZXJpYQpkaXNjYXJkZWRfZ2VuZXMgPC0gdW5kZXRlY3RlZF9nZW5lcyB8IGJhcmVseV9leHByZXNzZWRfZ2VuZXMKcHJpbnQocGFzdGUwKCJEaXNjYXJkZWQgZ2VuZXM6ICIsIHN1bShkaXNjYXJkZWRfZ2VuZXMpKSkKa2VwdF9nZW5lcyA8LSAhZGlzY2FyZGVkX2dlbmVzCnByaW50KHBhc3RlMCgiS2VwdCBnZW5lczogIiwgc3VtKGtlcHRfZ2VuZXMpKSkKCiMjIEdlbmVzIGFmdGVyIGZpbHRlcmluZwpmYV9leHByX2ZpbHRlcmVkIDwtIGZhX2V4cHJfcmF3W2tlcHRfZ2VuZXMsIF0KCmBgYAoKCgoKPCEtLSAjIyMjIFRNTSB2aWEgZWRnZVIgLS0+Cgo8IS0tIE5vdXMgYXBwbGlxdW9ucyBpY2kgbGEgbm9ybWFsaXNhdGlvbiBzdXIgYmFzZSBkZSBsYSBtw6l0aG9kZSBUTU0gKFRyaW1tZWQgTWVhbiBvZiB0aGUgTS12YWx1ZXMpIGTDqXZlbG9wcMOpZSBwYXIgUm9iaW5zb24gZXQgT3NobGFjay4gIC0tPgoKPCEtLSAtIFJvYmluc29uLCBNLkQuLCBPc2hsYWNrLCBBLiBBIHNjYWxpbmcgbm9ybWFsaXphdGlvbiBtZXRob2QgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIG9mIFJOQS1zZXEgZGF0YS4gR2Vub21lIEJpb2wgMTEsIFIyNSAoMjAxMCkuIDxodHRwczovL2RvaS5vcmcvMTAuMTE4Ni9nYi0yMDEwLTExLTMtcjI1PiAtLT4KCgoKPCEtLSBgYGB7ciB0bW1fc3RhbmRhcmRpc2F0aW9ufSAtLT4KPCEtLSBtZXNzYWdlKCJTdGFuZGFyZGlzaW5nIHRoZSBjb3VudHMgd2lodCBlZGdlUiBUTU0gbWV0aG9kIikgLS0+Cgo8IS0tICMjIENyZWF0ZSBhbiBlZGdlUiBvYmplY3Qgd2l0aCB0aGUgdHJhbnNjcmlwdG9tZSBkYXRhIC0tPgo8IS0tIGQgPC0gREdFTGlzdChjb3VudHMgPSBmYV9leHByX3JhdywgZ3JvdXAgPSBmYV9tZXRhJGNvbmRpdGlvbikgLS0+Cgo8IS0tICMjIENvbXB1dGUgdGhlIG5vcm1hbGlzaW5mZyBmYWN0b3JzIHdpdGggdGhlIFRNTSBtZXRob2QgLS0+CjwhLS0gZCA8LSBjYWxjTm9ybUZhY3RvcnMoZCwgbWV0aG9kID0gInVwcGVycXVhcnRpbGUiKSAjIyBDb21wdXRlIG5vcm1hbGl6aW5nIGZhY3RvcnMgLS0+CjwhLS0gIyBkX3RtbSA8LSBjYWxjTm9ybUZhY3RvcnMoZCwgbWV0aG9kID0gIlRNTSIpICMjIENvbXB1dGUgbm9ybWFsaXppbmcgZmFjdG9ycyAtLT4KPCEtLSAjIGRfcTMgPC0gY2FsY05vcm1GYWN0b3JzKGQsIG1ldGhvZCA9ICJ1cHBlcnF1YXJ0aWxlIikgIyMgQ29tcHV0ZSBub3JtYWxpemluZyBmYWN0b3JzIC0tPgo8IS0tICMgcGxvdChkX3RtbSRzYW1wbGVzJG5vcm0uZmFjdG9ycywgZF9xMyRzYW1wbGVzJG5vcm0uZmFjdG9ycykgLS0+CjwhLS0gIyBjcG1fcTMgPC0gY3BtKGRfcTMpIC0tPgo8IS0tICMgYXBwbHkoY3BtKGRfcTMpLCAyLCBxdWFudGlsZSwgcHJvYiA9IDAuNzUpIC0tPgo8IS0tICMgYXBwbHkoY3BtKGRfdG1tKSwgMiwgcXVhbnRpbGUsIHByb2IgPSAwLjc1KSAtLT4KCjwhLS0gIyMgU3RvcmUgdGhlIHNjYWxpbmcgZmFjdG9yIGluIHRoZSBtZXRhZGF0YSAtLT4KPCEtLSBmYV9tZXRhJHNjYWxpbmcuZmFjdG9yIDwtIGQkc2FtcGxlcyRub3JtLmZhY3RvcnMgLS0+Cgo8IS0tICMjIENyZWF0ZSBhIHNlcGFyYXRlIGRhdGEuZnJhbWUgd2l0aCB0aGUgc3RhbmRhcmRpc2VkIGNvdW50cyAtLT4KPCEtLSBmYV9leHByX3RtbSA8LSBmYV9leHByX3JhdyAjIyBBIHF1aWNrIHdheSB0byBoYXZlIHRoZSBzYW1lIHNpemUgYW5kIHJvdyBuYW1lcyBhbmQgY29sdW1uIG5hbWVzIC0tPgo8IS0tIGZvciAocyBpbiAxOm5jb2woZmFfZXhwcl90bW0pKSB7IC0tPgo8IS0tICAgZmFfZXhwcl90bW1bLCBzXSA8LSBmYV9leHByX3Jhd1ssIHNdIC8gZmFfbWV0YSRzY2FsaW5nLmZhY3RvcltzXSAtLT4KPCEtLSB9IC0tPgo8IS0tICMgcmFuZ2UoZmFfZXhwcl90bW0pIC0tPgo8IS0tICMgbWVhbih1bmxpc3QoZmFfZXhwcl90bW0pKSAtLT4KPCEtLSAjIHEzX3JhdyA8LSBhcHBseShmYV9leHByX3JhdywgMiwgcXVhbnRpbGUsIHByb2IgPSAwLjc1KSAtLT4KPCEtLSAjIHEzX3RtbSA8LSBhcHBseShmYV9leHByX3RtbSwgMiwgcXVhbnRpbGUsIHByb2IgPSAwLjc1KSAtLT4KPCEtLSAjIHBsb3QocTNfcmF3LCBxM190bW0pIC0tPgo8IS0tICMgc3VtbWFyeShmYV9leHByX3RtbSkgLS0+CjwhLS0gIyMgQ29tcHV0ZSB0aGUgbG9nMi10cmFuc2Zvcm1lZCBjb3VudHMgYWZ0ZXIgbm9ybWFsaXNhdGlvbiAtLT4KPCEtLSBmYV9leHByX3RtbV9sb2cyIDwtIGxvZzIoZmFfZXhwcl90bW0gKyBlcHNpbG9uKSAtLT4KCjwhLS0gYm94cGxvdChmYV9leHByX3RtbV9sb2cyLCBob3JpeiA9IFRSVUUpIC0tPgo8IS0tIGBgYCAtLT4KCgojIyMgU3RhbmRhcmRpc2F0aW9uIGVudHJlIMOpY2hhbnRpbGxvbnMKCk5vdXMgYXBwbGlxdW9ucyBpY2kgdW5lIG3DqXRob2RlIHNpbXBsZSBtYWlzIGVmZmljYWNlIGRlIHN0YW5kYXJkaXNhdGlvbgoKYGBge3Igbm9ybWFsaXNhdGlvbl9xM30KIyMjIyBJbnRlci1zYW1wbGUgc3RhbmRhcmRpc2F0aW9uIG9uIHRoZSBRMyBvZiByYXcgY291bnRzICMjIyMKdG90YWxfcTMgPC0gcXVhbnRpbGUodW5saXN0KGZhX2V4cHJfZmlsdGVyZWQpLCBwcm9icyA9IDAuNzUpCnNhbXBsZV9zdGF0X3ByZW5vcm0kUTNfZmlsdGVyZCA8LSBhcHBseShmYV9leHByX2ZpbHRlcmVkLCAyLCBxdWFudGlsZSwgcHJvYnMgPSAwLjc1KQpzYW1wbGVfc3RhdF9wcmVub3JtJHNjYWxlX2ZhY3RvciA8LSAxIC8gc2FtcGxlX3N0YXRfcHJlbm9ybSRRM19maWx0ZXJkICogdG90YWxfcTMKCiMjIEFwcGx5IHN0YW5kYXJkaXNhdGlvbgpmYV9leHByX3N0YW5kYXJkIDwtIHQodChmYV9leHByX2ZpbHRlcmVkKSAqIHVubGlzdChzYW1wbGVfc3RhdF9wcmVub3JtJHNjYWxlX2ZhY3RvcikpCiMjIENoZWNrIDNyZCBxdWFudGlsZSBhZnRlciBzdGFuZGFyZGlzYXRpb24Ka2FibGUoYXBwbHkoZmFfZXhwcl9zdGFuZGFyZCwgMiwgcXVhbnRpbGUsIHByb2JzID0gMC43NSksIAogICAgICBjb2wubmFtZXMgPSAiUTNfc3RhbmRhcmRpc2VkIiwgCiAgICAgIGNhcHRpb24gPSAiVGhpcmQgcXVhcnRpbGUgb2YgdGhlIGZpbHRlcmVkIGNvdW50cyBhZnRlciBpbnRlci1zYW1wbGUgc3RhbmRhcmRpc2F0aW9uIG9mIHRoZSB0aGlyZCBxdWFydGlsZXMuICIpCiMgYm94cGxvdChmYV9leHByX3N0YW5kYXJkLCBob3Jpem9udGFsID0gVFJVRSkKYGBgCgoKPCEtLSBgYGB7ciBzYW1wbGVfc3RhbmRhcmRpc2F0aW9ufSAtLT4KPCEtLSAjIyBHZW5lcmF0ZSBhIGRhdGEgZnJhbWUgd2hlcmUgbnVsbCB2YWx1ZXMgYXJlIHJlcGxhY2VkIGJ5IE5BIC0tPgo8IS0tIGZhX2V4cHJfbm9udWxsIDwtIGZhX2V4cHJfbG9nMl9maWx0ZXJlZCAtLT4KPCEtLSBmYV9leHByX25vbnVsbFtmYV9leHByX2xvZzJfZmlsdGVyZWQgPD0gMF0gPC0gTkEgLS0+CjwhLS0gc3VtKGlzLm5hKGZhX2V4cHJfbm9udWxsKSkgLS0+Cgo8IS0tICMjIENvbXB1dGUgdGhlIDNyZCBxdWFydGlsZSBvZiBub24tbnVsbCB2YWx1ZXMgZm9yIGVhY2ggc2FtcGxlIGFuZCBzdG9yZSB0aGVtIGluIGEgdmVjdG9yOiAtLT4KPCEtLSBzYW1wbGVfcTNfbm9udWxsIDwtIGFwcGx5KGZhX2V4cHJfbm9udWxsLCAyLCBxdWFudGlsZSwgcHJvYiA9IDAuNzUsIG5hLnJtID0gVFJVRSkgLS0+CjwhLS0gIyBwcmludChzYW1wbGVfcTNfbm9udWxsKSAtLT4KCjwhLS0gIyMgQ29tcHV0ZSB0aGUgUTMgZm9yIGFsbCB0aGUgdmFsdWVzLCB3aGljaCB3aWxsIHNlcnZlIGFzIHRhcmdldCB2YWx1ZSBmb3IgdGhlIHN0YW5kYXJkaXNlZCBzYW1wbGUgUTMgLS0+CjwhLS0gYWxsX3EzX25vbnVsbCA8LSBxdWFudGlsZSh1bmxpc3QoZmFfZXhwcl9ub251bGwpLCBwcm9iID0gMC43NSwgbmEucm0gPSBUUlVFKSAtLT4KPCEtLSAjIHByaW50KGFsbF9xM19ub251bGwpIC0tPgoKPCEtLSAjIyBTdGFuZGFyZGlzZSBleHByZXNzaW9uIG9uIHRoZSB0aGlyZCBxdWFydGlsZSBvZiBub24tbnVsbCB2YWx1ZXMgLS0+CjwhLS0gIyMgQmV3YXJlIDogZm9yIHRoaXMgc3RhbmRhcmRpemF0aW9uIHdlIGtlZXAgdGhlIG51bGwgdmFsdWVzIC0tPgo8IS0tICMjIFRyaWNrIDogd2UgdHJhbnNwb3NlIHRoZSB0YWJsZSB0byBhcHBseSB0aGUgcmF0aW8gc2FtcGxlIHBlciBzYW1wbGUsICAtLT4KPCEtLSAjIyBhbmQgdGhlbiB0cmFuc3Bvc2UgdGhlIHJlc3VsdHMgdG8gZ2V0IGJhY2sgdGhlIGdlbmVzIGluIHJvd3MgYW5kIHNhbXBsZXMgaW4gY29sdW1ucyAtLT4KPCEtLSBmYV9leHByX2xvZzIgPC0gdCh0KGZhX2V4cHJfbG9nMl9maWx0ZXJlZCkgKiBhbGxfcTNfbm9udWxsIC8gc2FtcGxlX3EzX25vbnVsbCApIC0tPgo8IS0tICMgcXVhbnRpbGUodW5saXN0KGZhX2V4cHJfbG9nMiksIHByb2JzID0gMC43NSwgbmEucm0gPSBUUlVFKSAtLT4KCjwhLS0gIyMgV2UgYWxzbyBjb21wdXRlIHRoZSB2YWx1ZXMgZm9yIHRoZSAibm9udWxsIiB0YWJsZSBmb3IgIC0tPgo8IS0tICMjIHRoZSBzYWtlIG9mIGNvbXBhcmlzb24gYW5kIHRvIGNoZWNrIHRoYXQgdGhlIHRoaXJkIHF1YW50aWxlcyBvZiBub24tbnVsbCAgLS0+CjwhLS0gIyMgdmFsdWVzIGFyZSB3ZWxsIGlkZW50aWNhbCBhY3Jvc3Mgc2FtcGxlcy4gLS0+CjwhLS0gZmFfZXhwcl9sb2cyX25vbnVsbCA8LSB0KHQoZmFfZXhwcl9ub251bGwpICogYWxsX3EzX25vbnVsbCAvIHNhbXBsZV9xM19ub251bGwgKSAtLT4KPCEtLSAjIHF1YW50aWxlKHVubGlzdChmYV9leHByX2xvZzJfbm9udWxsKSwgcHJvYnMgPSAwLjc1LCBuYS5ybSA9IFRSVUUpIC0tPgoKPCEtLSAjIyBDb21wdXRlIFEzIGJlZm9yZSBhbmQgYWZ0ZXIgc3RhbmRhcmRpc2F0aW9uLCBpbmNsdWRpbmcgb3Igbm90IHRoZSBudWxsIHZhbHVlcyAtLT4KPCEtLSBzdGFuZGFyZGlzYXRpb25faW1wYWN0IDwtIGRhdGEuZnJhbWUoIC0tPgo8IS0tICAgYmVmb3JlX2FsbCA9IGFwcGx5KGZhX2V4cHJfbG9nMl9maWx0ZXJlZCwgMiwgcXVhbnRpbGUsIHByb2IgPSAgMC43NSwgbmEucm0gPSBUUlVFKSwgLS0+CjwhLS0gICBiZWZvcmVfbm9udWxsID0gYXBwbHkoZmFfZXhwcl9ub251bGwsIDIsIHF1YW50aWxlLCBwcm9iID0gIDAuNzUsIG5hLnJtID0gVFJVRSksIC0tPgo8IS0tICAgYWZ0ZXJfbm9udWwgPSBhcHBseShmYV9leHByX2xvZzJfbm9udWxsLCAyLCBxdWFudGlsZSwgcHJvYiA9ICAwLjc1LCBuYS5ybSA9IFRSVUUpLCAtLT4KPCEtLSAgIGFmdGVyX2FsbCA9IGFwcGx5KGZhX2V4cHJfbG9nMiwgMiwgcXVhbnRpbGUsIHByb2IgPSAgMC43NSwgbmEucm0gPSBUUlVFKSAtLT4KPCEtLSApIC0tPgoKPCEtLSAjIyBOb3RlOiBhZnRlciBzdGFuZGFyZGl6YXRpb24gdGhlIFEzIG9mIHRoZSBkYXRhIHNob3cgc29tZSB2YXJpYXRpb25zICAtLT4KPCEtLSAjIyBiZWNhdXNlIHdlIGNvbXB1dGUgdGhlbSBoZXJlIHdpdGggdGhlIG51bGwgdmFsdWVzIC0tPgo8IS0tIGthYmxlKHN0YW5kYXJkaXNhdGlvbl9pbXBhY3QsIGNhcHRpb24gPSAiSW1wYWN0IG9mIHN0YW5kYXJkaXphdGlvbiBvbiB0aGUgdGhpcmQgcXVhbnRpbGUgKFEzKSBwZXIgc2FtcGxlLiBUaGlyZCBxdWFudGlsZXMgYXJlIGNvbXB1dGVkIGJlZm9yZSBhbmQgYWZ0ZXIgc3RhbmRhcmRpc2F0aW9uLCB3aXRoIGVpdGhlciBhbGwgdGhlIHZhbHVlcyBvZiB0aGUgZmlsdGVyZWQgdGFibGUsIG9yIG9ubHkgdGhlIG5vbi1udWxsIHZhbHVlcy4gIikgLS0+CjwhLS0gYGBgIC0tPgoKCiMjIyBUcmFuc2Zvcm1hdGlvbiBsb2cyCgpOb3VzIGFwcGxpcXVvbnMgdW5lIHRyYW5zZm9ybWF0aW9uIGVuIGxvZzIgZGVzIGRvbm7DqWVzIGJydXRlcywgYXByw6hzIGF2b2lyIGFqb3V0w6kgdW4gZXBzaWxvbiAkXGVwc2lsb24gPSAxJCAobGVzIHZhbGV1cnMgbnVsbGVzIHNlcm9udCBkb25jIHJlcHLDqXNlbnTDqWVzIHBhciB1biBsb2cyKGNvdW50cykgdmFsYW50ICQwJC4gTm91cyBzdG9ja29ucyBsZSByw6lzdWx0YXQgZGFucyB1biBkYXRhLmZyYW1lIG5vbW3DqSBgZmFfZXhwcl9sb2cyYC4KCk5vdXMgYWZmaWNob25zIHVuIGZyYWdtZW50IGRlcyB0YWJsZWF1eCBgZmFfZXhwcl9yYXdgIGV0IGBmYV9leHByX2xvZzJgIGVuIHPDqWxlY3Rpb25uYW50IGxlcyBsaWduZXMgMTAwIMOgIDEwOSBldCBsZXMgY29sb25uZXMgNSDDoCAxMCwgYWZpbiBkZSBub3VzIGFzc3VyZXIgcXVlIGxhIHRyYW5zZm9ybWF0aW9uIGVuIGxvZzIgYSBiaWVuIGZvbmN0aW9ubsOpLiAKCmBgYHtyIGxvZzJfdHJhbnNmb3JtfQojIyBMb2cyIHRyYW5zZm9ybWF0aW9uIG9mIHRoZSB0cmFuc2NyaXB0b21lIGRhdGEKZXBzaWxvbiA8LSAxCmZhX2V4cHJfbG9nMiA8LSBsb2cyKGZhX2V4cHJfc3RhbmRhcmQgKyBlcHNpbG9uKQojIGRpbShmYV9leHByX2xvZzIpCiMgVmlldyhoZWFkKGZhX2V4cHJfbG9nMikpCgojIyBEaXNwbGF5IG9mIGEgZnJhZ21lbnQgb2YgdGhlIGRhdGEgYmVmb3JlIGFuZCBhZnRlciBsb2cyIHRyYW5zZm9ybWF0aW9uCmthYmxlKGZhX2V4cHJfcmF3WzEwMDoxMDksIDU6MTBdLCBjYXB0aW9uID0gIkZyYWdtZW50IGRlcyBkb25uw6llcyB0cmFuc2NyaXB0b21pcXVlcyBicnV0ZXMiKQprYWJsZShmYV9leHByX2xvZzJbMTAwOjEwOSwgNToxMF0sIGNhcHRpb24gPSAiRnJhZ21lbnQgZGVzIGRvbm7DqWVzIHRyYW5zY3JpcHRvbWlxdWVzIGFwcsOocyB0cmFuc2Zvcm1hdGlvbiBsb2cyIikKYGBgCgojIyAzLiBTdGF0aXN0aXF1ZXMgZGVzY3JpcHRpdmVzIHN1ciBsZXMgZG9ubsOpZXMgbm9ybWFsaXPDqWVzCgoqKipBIHZvdXMgZGUgam91ZXIhKioqCgojIyMgU3RhdGlzdGlxdWVzIHBhciBnw6huZSBhcHLDqHMgbm9ybWFsaXNhdGlvbgoKR8OpbsOpcmV6IHVuIGRhdGEuZnJhbWUgYXZlYyB1bmUgbGlnbmUgcGFyIGfDqG5lIMOgIHBhcnRpciBkdSB0YWJsZWF1IGRlIGRvbm7DqWVzIG5vcm1hbGlzw6llcywgYXZlYyBsZXMgc3RhdGlzdGlxdWVzIHN1aXZhbnRlcyAodW5lIHN0YXRpc3RpcXVlIHBhciBjb2xvbm5lKToKCi0gbW95ZW5uZQotIHZhcmlhbmNlCi0gw6ljYXJ0LXR5cGUKLSBjb2VmZmljaWVudCBkZSB2YXJpYXRpb24gKMOpY2FydC10eXBlIGRpdmlzw6kgcGFyIGxhIG1veWVubmUpCi0gaW50ZXJ2YWxsZSBpbnRlci1xdWFydGlsZXMKLSBtaW5pbXVtCi0gbcOpZGlhbmUKLSBtYXhpbXVtCgpgYGB7ciBnZW5lX3N0YXRfcG9zdF9ub3JtfQojIyBHZW5lLXdpc2Ugc3RhdGlzdGljcyBhZnRlciBub3JtYWxpc2F0aW9uCm1lc3NhZ2UoIkNvbXB1dGluZyBnZW5lLXdpc2Ugc3RhdGlzdGljcyBvbiBsb2cyLXRyYW5zZm9ybWVkIGFuZCBub3JtYWxpc2VkIGNvdW50cyIpCmdlbmVfc3RhdF9ub3JtIDwtIGRhdGEuZnJhbWUoCiAgbWVhbiA9IGFwcGx5KGZhX2V4cHJfbG9nMiwgMSwgbWVhbiwgbmEucm0gPSBGKSwKICB2YXIgPSBhcHBseShmYV9leHByX2xvZzIsIDEsIHZhciwgbmEucm0gPSBUUlVFKSwKICBzZCA9IGFwcGx5KGZhX2V4cHJfbG9nMiwgMSwgc2QsIG5hLnJtID0gRiksCiAgQ1YgPSBOQSwKICBtaW4gPSBhcHBseShmYV9leHByX2xvZzIsIDEsIG1pbiwgbmEucm0gPSBUUlVFKSwKICBRMSA9IGFwcGx5KGZhX2V4cHJfbG9nMiwgMSwgcXVhbnRpbGUsIHAgPSAwLjI1LCBuYS5ybSA9IFRSVUUpLAogIG1lZGlhbiA9IGFwcGx5KGZhX2V4cHJfbG9nMiwgMSwgbWVkaWFuLCBuYS5ybSA9IFRSVUUpLAogIFEzID0gYXBwbHkoZmFfZXhwcl9sb2cyLCAxLCBxdWFudGlsZSwgcCA9IDAuNzUsIG5hLnJtID0gVFJVRSksCiAgbWF4ID0gYXBwbHkoZmFfZXhwcl9sb2cyLCAxLCBtYXgsIG5hLnJtID0gVFJVRSksCiAgbnVsbCA9IGFwcGx5KGZhX2V4cHJfbG9nMiA9PSAwLCAxLCBzdW0sIG5hLnJtID0gVFJVRSkKICApCmdlbmVfc3RhdF9ub3JtJENWIDwtIGdlbmVfc3RhdF9ub3JtJHNkIC8gZ2VuZV9zdGF0X25vcm0kbWVhbgpgYGAKCiMjIyBBbm5vdGF0aW9uIGRlcyBnw6huZXMKCkNoYXF1ZSBnw6huZSDDqXRhbnQgZG9ubsOpIHBhciBzb24gaWRlbnRpZmlhbnQgZGFucyBsYSBiYXNlIGRlIGRvbm7DqWVzIEVOU0VNQkwgdm91cyB1dGlsaXNlcmV6IGxlIGBwYXF1ZXQgYmlvbWFSdCBkZSBiaW9jb25kdWN0b3JgIHBvdXIgYWpvdXRlciBkZXMgYW5ub3RhdGlvbnMgOiBzeW1ib2xlLCBjaHJvbW9zb21lLCBjb29yZG9ubsOpZXMgZ8Opbm9taXF1ZXMsIGJyaW4uIApTdWl2ZXogcGFzIMOgIHBhcyBsYSBtw6l0aG9kZSBwcm9wb3PDqWUgKCoqKmNlcnRhaW5lcyDDqXRhcGVzIHBldXZlbnQgcHJlbmRyZSBxdWVscXVlcyBtaW51dGVzKioqKToKCiAtIGNoYXJnZXogbGUgcGFxdWV0IGJpb21hUnQsIHZvaXJlIGluc3RhbGxlci1sZSB1bmlxdWVtZW50IHNpIG7DqWNlc3NhaXJlLiBJbmRpcXVleiBsZSBjb2RlIMOgIGwnZW1wbGFjZW1lbnQgYWTDqXF1YXQgZGFucyBkZSAuUm1kLgoKIC0gc8OpbGVjdGlvbm5leiBsYSBiYXNlIGRlIGRvbm7DqWVzIEVOU0VNQkwgYXZlYyBsYSBmb25jdGlvbiBgdXNlTWFydCgpYC4gQXR0ZW50aW9uIMOgIGNob2lzaXIgbGUgYm9uIGfDqW5vbWUgYXZlYyBsJ2FndW1lbnQgYGRhdGFzZXRgOiBgbW11c2N1bHVzX2dlbmVfZW5zZW1ibGAKIAogLSBhdmVjIGxhIGZvbmN0aW9uIGBnZXRCTSgpYCByw6ljdXDDqXJleiBkZSBsYSBiYXNlIGRlIGRvbm7DqWVzIEVOU0VNQkwgbGVzIGNoYW1wcyBkZW1hbmTDqXMgKCoqKnBvdXIgc3ltYm9sZSB1dGlsaXNleiBleHRlcm5hbF9nZW5lX25hbWUqKiopIGVuIGFwcGxpcXVhbnQgImVuc2VtYmxfZ2VuZWlkIiBwb3VyIGwnYWd1bWVudCBgZmlsdGVyYCBldCBlbiBpbmRpcXVhbnQgcG91ciBsJ2FyZ3VtZW50IGB2YWx1ZXNgIGxlIHZlY3RldXIgZGVzIGlkZW50aWZpYW50cyBkZXMgZ8OobmVzIHByw6lzZW50cyBkYW5zIGxlIGRhdGFmcmFtZSBgZ2VuZV9zdGF0X25vcm1gLiBWb3VzIG9idGVuZXogdW4gZGF0YWZyYW1lLgogCkEgcHLDqXNlbnQsIGFqb3V0ZXogYXUgZGF0YWZyYW1lIGBnZW5lX3N0YXRfbm9ybWAgZW4gMcOocmVzIGNvbG9ubmVzIGxlcyBhbm5vdGF0aW9ucyByZXRyb3V2w6llcyBncsOiY2Ugw6AgYmlvbWFSdC4gQXR0ZW50aW9uLCBjZXJ0YWlucyBnw6huZXMgbmUgc29udCBwYXMgcmV0cm91dsOpcyBkYW5zIGxhIHZlcnNpb24gZCdFTlNFTUJMIHN1ciBiaW9tYVJ0IGRvbmMgbGFpc3NleiBkZXMgTkEgY29tbWUgZG9ubsOpZXMgbWFucXVhbnRlcyBkYW5zIGNlIGNhcy4gTm91cyB2b3VzIHJlY29tbWFuZG9ucyBkJ3V0aWxpc2VyIGxhIGZ1bmN0aW9uIGBtZXJnZSgpYCBkZSBSIGJhc2Ugb3UgYmllbiBgbGVmdF9qb2luKClgIGRlIGBkcGx5cmAgcG91ciBmdXNpb25uZXIgbGVzIGRldXggZGF0YWZyYW1lcyBlbiB1biBzZXVsLgogCmBgYHtyIGdlbmVfYW5ub3RhdGlvbnMsIGV2YWw9RkFMU0V9CiMjIyBHZW5lIGFubm90YXRpb25zICMjIyMKCiMjIE9wZW4gYSBjb25uZWN0aW9uIHRvIEVuc2VtYmwgTUFSVAptZXNzYWdlKCJPcGVuaW5nIGNvbm5lY3Rpb24gdG8gRW5zZW1ibCBNQVJUIikKZW5zZW1ibCA8LSB1c2VNYXJ0KCJFTlNFTUJMX01BUlRfRU5TRU1CTCIsIAogICAgICAgICAgICAgICAgICAgaG9zdCA9ICJ3d3cuZW5zZW1ibC5vcmciLCAKICAgICAgICAgICAgICAgICAgIGRhdGFzZXQgPSAibW11c2N1bHVzX2dlbmVfZW5zZW1ibCIpCgojIyBHZXQgZ2VuZSBhbm5vdGF0aW9ucwptZXNzYWdlKCJHZXR0aW5nIGdlbmUgYW5ub3RhdGlvbnMiKQpnZW5lcyA8LSBnZXRCTShhdHRyaWJ1dGVzID0gYygiZW5zZW1ibF9nZW5lX2lkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJleHRlcm5hbF9nZW5lX25hbWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNocm9tb3NvbWVfbmFtZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdGFydF9wb3NpdGlvbiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZW5kX3Bvc2l0aW9uIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJhbmQiKSwgCiAgICAgICAgICAgICAgIGZpbHRlciA9ICJlbnNlbWJsX2dlbmVfaWQiLAogICAgICAgICAgICAgICB2YWx1ZXMgPSByb3cubmFtZXMoZ2VuZV9zdGF0X25vcm0pLAogICAgICAgICAgICAgICBtYXJ0ID0gZW5zZW1ibCkKICAKIyMgTWVyZ2UgZ2VuZSBhbm5vdGF0aW9ucyBhbmQgZXhwcmVzc2lvbiBzdGF0aXN0aWNzCmdlbmVfc3RhdF9ub3JtIDwtIG1lcmdlKGdlbmVzLCBnZW5lX3N0YXRfbm9ybSwgYnkueCA9ICJlbnNlbWJsX2dlbmVfaWQiLCBieS55ID0gMCwgc29ydCA9IEZBTFNFKQoKa2FibGUoZ2VuZV9zdGF0X25vcm1bMTAwOjEwOSwgXSwgY2FwdGlvbiA9ICJHZW5lLXdpc2Ugc3RhdGlzdGljcyBhZnRlciBub3JtYWxpc2F0aW9uIikKYGBgCgoqKkNoYWxsZW5nZSBmYWxjdWx0YXRpZjoqKgoKUsOpb3Jkb25uZXogbGVzIGfDqG5lcyBwYXIgcG9zaXRpb24gZ8Opbm9taXF1ZSBldCBhZmZpY2hleiBsZXMgbGlnbmVzIDUgcHJlbWnDqHJlcyBldCAgNSBkZXJuacOocmVzIGxpZ25lcyBkZSBjZSB0YWJsZWF1IGRlIHN0YXRpc3RpcXVlcy4gCgpgYGB7ciBzb3J0X2dlbmVzX2J5X2Nocm9tLCBldmFsPUZBTFNFfQoKZ2VuZV9zdGF0X25vcm0gPC0gZ2VuZV9zdGF0X25vcm1bb3JkZXIoZ2VuZV9zdGF0X25vcm0kY2hyb21vc29tZV9uYW1lLCBnZW5lX3N0YXRfbm9ybSRzdGFydF9wb3NpdGlvbiksXQoKa2FibGUoZ2VuZV9zdGF0X25vcm1bYygxOjUsIChucm93KGdlbmVfc3RhdF9ub3JtKSAtIDQpOm5yb3coZ2VuZV9zdGF0X25vcm0pKSwgXSwgCiAgICAgIGNhcHRpb24gPSAiR2VuZS13aXNlIHN0YXRpc3RpY3MgYWZ0ZXIgbm9ybWFsaXNhdGlvbi4iKQpgYGAKCgojIyMgRGlzdHJpYnV0aW9uIGRlcyBkb25uw6llcwoKLSBEZXNzaW5leiBzb3VzIGZvcm1lIGQndW4gaGlzdG9ncmFtbWUgbGEgZGlzdHJpYnV0aW9uIGRlcyB2YWxldXJzIGFwcsOocyBub3JtYWxpc2F0aW9uICh0b3VzIMOpY2hhbnRpbGxvbnMgY29uZm9uZHVzKQoKYGBge3IgZmFfZXhwcl9ub3JtX2Rpc3RyaWIsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTUsIG91dC53aWR0aD0iNzAlIiwgZmlnLmNhcD0iRGlzdHJpYnV0aW9uIG9mIGV4cHJlc3Npb24gdmFsdWVzIChsb2cyIGNvdW50cykgYWZ0ZXIgZ2VuZSBmaWx0ZXJpbmcgYW5kIHN0YW5kYXJkaXNhdGlvbiBvbiB0aGUgc2FtcGxlLXdpc2UgdGhpcmQtcXVhcnRpbGUgb2Ygbm9uLW51bGwgdmFsdWVzLiBUaGUgdmVydGljYWwgbGluZSBoaWdobGlnaHRzIHRoZSBtZWFuIHZhbHVlLiAifQpoaXN0KHVubGlzdChmYV9leHByX2xvZzIpLCAKICAgICBicmVha3MgPSBzZXEoZnJvbSA9IDAsIHRvID0gbWF4KGZhX2V4cHJfbG9nMikgKyAxLCBieSA9IDAuMjUpLAogICAgIHhsYWIgPSAibG9nMihjb3VudHMpIGFmdGVyIHN0YW5kYXJkaXNhdGlvbiIsIAogICAgIHlsYWIgPSAibnVtYmVyIG9mIGdlbmVzIGFmdGVyIGZpbHRlcmluZyIsCiAgICAgY29sID0gIiNCQkRERkYiLAogICAgIGxhcyA9IDEsIGNleC5heGlzID0gMC44LAogICAgIG1haW4gPSAiZGlzdHJpYnV0aW9uIGFmdGVyIHN0YW5kYXJkaXNhdGlvbiIpCmFibGluZSh2ID0gbWVhbihmYV9leHByX2xvZzIpLCBjb2wgPSAiZGFya2dyZWVuIiwgbHdkID0gMikKYGBgCgotIERlc3NpbmV6IHVuIGJveCBwbG90IHBhciDDqWNoYW50aWxsb24gYXZhbnQgZXQgYXByw6hzIG5vcm1hbGlzYXRpb24sIGV0IGNvbW1lbnRleiBsYSBmYcOnb24gZG9udCBsJ2VmZmV0IGRlIGxhIG5vcm1hbGlzYXRpb24gYXBwYXJhw650IHN1ciBjZXMgZ3JhcGhpcXVlcy4gCgpgYGB7ciBib3hwbG90c19zdGFuZGFyZGlzYXRpb25faW1wYWN0LCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTIsIG91dC53aWR0aD0iMTAwJSIsIGZpZy5jYXA9IkJveCBwbG90cyBzaG93aW5nIHRoZSBpbXBhY3Qgb2Ygbm9ybWFsaXNhdGlvbiJ9CiMjIyMgQm94IHBsb3RzIHRvIHNob3cgbm9ybWFsaXNhdGlvbiBpbXBhY3QgIyMjIwpwYXIobWFyID0gYyg0LDYsNCwxKSkgIyMgU2V0IHRoZSBtYXJnaW5zCnBhcihtZnJvdyA9IGMoMiwyKSkKYm94cGxvdChmYV9leHByX3JhdywgCiAgICAgICAgaG9yaXpvbnRhbCA9IFRSVUUsCiAgICAgICAgeGxhYiA9ICJjb3VudHMiLCAKICAgICAgICBsYXMgPSAxLCAKICAgICAgICBjb2wgPSBmYV9tZXRhJGNvbG9yLCAKICAgICAgICBtYWluID0gIlJhdyBjb3VudHMsIGFsbCBnZW5lcyIpCmJveHBsb3QoZmFfZXhwcl9maWx0ZXJlZCwgCiAgICAgICAgaG9yaXpvbnRhbCA9IFRSVUUsIAogICAgICAgIHhsYWIgPSAiY291bnRzIiwgCiAgICAgICAgbGFzID0gMSwgCiAgICAgICAgY29sID0gZmFfbWV0YSRjb2xvciwgCiAgICAgICAgbWFpbiA9ICJSYXcgY291bnRzLCBmaWx0ZXJlZCBnZW5lcyIpCmJveHBsb3QoZmFfZXhwcl9zdGFuZGFyZCwgCiAgICAgICAgaG9yaXpvbnRhbCA9IFRSVUUsIAogICAgICAgIHhsYWIgPSAiY291bnRzIiwgCiAgICAgICAgbGFzID0gMSwgCiAgICAgICAgY29sID0gZmFfbWV0YSRjb2xvciwgCiAgICAgICAgbWFpbiA9ICJTdGFuZGFyZGlzZWQgY291bnRzIChzYW1wbGUgUTMpIikKYm94cGxvdChmYV9leHByX2xvZzIsIAogICAgICAgIHhsYWIgPSAibG9nMihjb3VudHMpIiwgCiAgICAgICAgbGFzID0gMSwgCiAgICAgICAgaG9yaXpvbnRhbCA9IFRSVUUsIAogICAgICAgIGNvbCA9IGZhX21ldGEkY29sb3IsIAogICAgICAgIG1haW4gPSAiTm9ybWFsaXNlZCBjb3VudHMiKQpwYXIobWZyb3cgPSBjKDEsIDEpKQpwYXIobWFyID0gYyg0LDUsNSwxKSkKYGBgCgojIyA0LiBBbmFseXNlIGRlIHJlZ3JvdXBlbWVudCBkZXMgZG9ubsOpZXMKCioqKkEgdm91cyBkZSBqb3VlciEqKioKCiMjIyBTw6lsZWN0aW9uIGRlIGfDqG5lcyBkJ2V4cHJlc3Npb24gw6lsZXbDqWUgZXQgdmFyaWFibGUKClBvdXIgcsOpZHVpcmUgbGUgbm9tYnJlIGRlIGfDqG5lcywgbm91cyBhbGxvbnMgw6ljYXJ0ZXIgbGVzIGfDqG5lcyBmYWlibGVtZW50IGV4cHJpbcOpcyAobG9nMiBtb3llbiBpbmbDqXJpZXVyIMOgIDQpLCBldCBuZSByZXRlbmlyIHF1ZSBjZXV4IHF1aSBtb250cmVudCBkZXMgdmFyaWF0aW9ucyBpbXBvcnRhbnRlcyBlbnRyZSDDqWNoYW50aWxsb25zLiBQb3VyIGNlIGRlcm5pZXIgY3JpdMOocmUsIG5vdXMgbm91cyBiYXNvbnMgc3VyIGxhIHZhcmlhbmNlLiAKCjwhLS0gU8OpbGVjdGlvbm5leiBsZXMgZ8OobmVzIGF5YW50IHVuIG5pdmVhdSBsb2cyIG1veWVuIG1pbmltYWwgc3Vww6lyaWV1ciDDoCAzICgkbSA+IDMkKSBldCB1biBjb2VmZmljaWVudCBkZSB2YXJpYXRpb24gc3Vww6lyaWV1ciDDoCAwLjUgKCRDViA+IDAuNSQpLiBOb3RlOiBjZXMgdmFsZXVycyBzb250IHBhcmZhaXRlbWVudCBhcmJpdHJhaXJlcywgZWxsZXMgb250IMOpdMOpIGNob2lzaWVzIHBvdXIgb2J0ZW5pciB1biBub21icmUgcmFpc29ubmFibGUgZGUgZ8OobmVzLiAgLS0+CgpTw6lsZWN0aW9ubmV6IGxlcyBnw6huZXMgYXlhbnQgdW4gbml2ZWF1IGxvZzIgbW95ZW4gbWluaW1hbCBzdXDDqXJpZXVyIMOgIDUgKCRtID4gNSQpIGV0IHVuZSB2YXJpYW5jZSBzdXDDqXJpZXVyZSDDoCAyICgkc14yID4gMiQpLiBOb3RlOiBjZXMgdmFsZXVycyBzb250IHBhcmZhaXRlbWVudCBhcmJpdHJhaXJlcywgZWxsZXMgb250IMOpdMOpIGNob2lzaWVzIHBvdXIgb2J0ZW5pciB1biBub21icmUgcmFpc29ubmFibGUgZGUgZ8OobmVzLiAKCmBgYHtyIGdlbmVfc2VsZWN0aW9ufQojIyBDb21wdXRlIGEgQm9vbGVhbiB2ZWN0b3IgaW5kaWNhdGluZyB3aGV0aGVyIGVhY2ggZ2VuZSBwYXNzZXMgb3Igbm90IHRoZSBleHByZXNzaW9uIGxldmVsIHRocmVzaG9sZApoaWdoX2V4cHJlc3Npb24gPC0gZ2VuZV9zdGF0X25vcm0kbWVhbiA+IDUKIyB0YWJsZShoaWdoX2V4cHJlc3Npb24pICMgY291bnQgbnVtYmVyIG9mIGdlbmVzIHdpdGggaGlnaC93ZWFrIGV4cHJlc3Npb24KCiMjIENvbXB1dGUgYSBCb29sZWFuIHZlY3RvciBpbmRpY2F0aW5nIHdoZXRoZXIgZWFjaCBnZW5lIHBhc3NlcyBvciBub3QgdGhlIHZhcmlhdGlvbiBjb2VmZmljaWVudCB0aHJlc2hvbGQKaGlnaF92YXJpYXRpb24gPC0gZ2VuZV9zdGF0X25vcm0kQ1YgPiAwLjUKIyB0YWJsZShoaWdoX3ZhcmlhdGlvbikgIyBjb3VudCBudW1iZXIgb2YgZ2VuZXMgd2l0aCB3ZWFrIGhpZ2ggY29lZmZmaWNpZW50IG9mIHZhcmlhdGlvbgoKIyMgQ29tcHV0ZSBhIEJvb2xlYW4gdmVjdG9yIGluZGljYXRpbmcgd2hldGhlciBlYWNoIGdlbmUgcGFzc2VzIG9yIG5vdCB0aGUgdmFyaWFuY2UgdGhyZXNob2xkCmhpZ2hfdmFyaWFuY2UgPC0gZ2VuZV9zdGF0X25vcm0kdmFyID4gMgojIHRhYmxlKGhpZ2hfdmFyaWFuY2UpICMgY291bnQgbnVtYmVyIG9mIGdlbmVzIHdpdGggd2VhayBoaWdoIHZhcmlhbmNlCgojICMjIFNlbGVjdCBnZW5lcyBoYXZpbmcgYm90aCBhIGhpZ2ggbWVhbiBleHByZXNzaW9uIGFuZCBhIGhpZ2ggdmFyaWF0aW9uIGNvZWZmaWNpZW4KIyBzZWxlY3RlZF9nZW5lcyA8LSBoaWdoX3ZhcmlhdGlvbiAmIGhpZ2hfZXhwcmVzc2lvbgojIyBTZWxlY3QgZ2VuZXMgaGF2aW5nIGJvdGggYSBoaWdoIG1lYW4gZXhwcmVzc2lvbiBhbmQgYSBoaWdoIHZhcmlhbmNlCnNlbGVjdGVkX2dlbmVzIDwtIGhpZ2hfdmFyaWFuY2UgJiBoaWdoX2V4cHJlc3Npb24KIyB0YWJsZShzZWxlY3RlZF9nZW5lcykgIyBjb3VudCBudW1iZXIgb2YgZ2VuZXMgd2l0aCB3ZWFrIGhpZ2ggY29lZmZmaWNpZW50IG9mIHZhcmlhdGlvbgpwcmludChwYXN0ZTAoIlNlbGVjdGVkIGdlbmVzOiAiLCBzdW0oc2VsZWN0ZWRfZ2VuZXMpKSkKCiMjIENyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCB0aGUgZXhwcmVzc2lvbiBvZiB0aGUgc2VsZWN0ZWQgZ2VuZXMKZmFfZXhwcl9zZWxlY3RlZCA8LSBmYV9leHByX2xvZzJbc2VsZWN0ZWRfZ2VuZXMsIF0KYGBgCgpEZXNzaW5leiBkZXMgaGlzdG9ncmFtbWVzIGRlcyB2YWxldXJzIGQnZXhwcmVzc2lvbiBhdmFudCBldCBhcHLDqHMgY2V0dGUgc8OpbGVjdGlvbiBkZSBnw6huZXMsIGV0IGNvbW1lbnRleiBsZXMgZGlmZsOpcmVuY2VzLiAKCmBgYHtyIGhpc3RfZXhwcl9zZWxlY3RlZF9nZW5lcywgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9Niwgb3V0LndpZHRoPSIxMDAlIiwgZmlnLmNhcD0iRGlzdHJpYnV0aW9uIG9mIGV4cHJlc3Npb24gdmFsdWVzIGJlZm9yZSBhbmQgYWZ0ZXIgZ2VuZSBzZWxlY3Rpb24ifQojIyMjIEhpc3RvZ3JhbXMgb2YgZXhwcmVzc2lvbiBiZWZvcmUgYW5kIGFmdGVyIGdlbmUgc2VsZWN0aW9uICMjIyMKCnBhcihtZnJvdyA9IGMoMiwxKSkKaGlzdCh1bmxpc3QoZmFfZXhwcl9sb2cyKSwgCiAgICAgYnJlYWtzID0gc2VxKGZyb20gPSAwLCB0byA9IG1heChmYV9leHByX2xvZzIpICsgMSwgYnkgPSAwLjI1KSwKICAgICBsYXMgPSAxLCAKICAgICBjZXguYXhpcyA9IDAuOCwgCiAgICAgbWFpbiA9ICJTdGFuZGFyZGl6ZWQgdmFsdWVzIGJlZm9yZSBnZW5lIHNlbGVjdGlvbiIsCiAgICAgY29sID0gICIjRERCQkZGIikKCmhpc3QodW5saXN0KGZhX2V4cHJfc2VsZWN0ZWQpLCAKICAgICBicmVha3MgPSBzZXEoZnJvbSA9IDAsIHRvID0gbWF4KGZhX2V4cHJfbG9nMikgKyAxLCBieSA9IDAuMjUpLAogICAgIGxhcyA9IDEsIAogICAgIGNleC5heGlzID0gMC44LCAKICAgICBtYWluID0gIlN0YW5kYXJkaXplZCB2YWx1ZXMgYWZ0ZXIgZ2VuZSBzZWxlY3Rpb24iLAogICAgIGNvbCA9ICAiI0ZGRERCQiIpCgpwYXIobWZyb3cgPSBjKDEsMSkpCgoKIyAjIyBTb21lIHF1aWNrIGNoZWNrczogdGhlIHNlbGVjdGlvbiBvZiBoaWdobHkgdmFyaWFibGUgZ2VuZXMgc2VsZWN0IHRob3NlIGhhdmluZyBtYW55IHplcm9zIC0gYW5kIGhpZ2ggdmFsdWVzIGluIG90aGVyIHNhbXBsZXMKIyBoaXN0KHVubGlzdChmYV9leHByX2xvZzJfZmlsdGVyZWRbaGlnaF9leHByZXNzaW9uLCBdKSwgYnJlYWtzPTEwMCkKIyBoaXN0KHVubGlzdChmYV9leHByX2xvZzJfZmlsdGVyZWRbaGlnaF92YXJpYXRpb24sIF0pLCBicmVha3M9MTAwKQojIGhpc3QodW5saXN0KGZhX2V4cHJfbG9nMl9maWx0ZXJlZFshaGlnaF92YXJpYXRpb24sIF0pLCBicmVha3M9MTAwKQojIGhpc3QodW5saXN0KGZhX2V4cHJfbG9nMl9maWx0ZXJlZFtzZWxlY3RlZF9nZW5lcywgXSksIGJyZWFrcz0xMDApCmBgYAoKRGVzc2luZXogdW4gYm94IHBsb3QgcGFyIMOpY2hhbnRpbGxvbiBkZXMgdmFsZXVycyBkJ2V4cHJlc3Npb24gYXZhbnQgZXQgYXByw6hzIHPDqWxlY3Rpb24gZGVzIGfDqG5lcywgZXQgY29tbWVudGV6IGxlIHLDqXN1bHRhdC4gCgpgYGB7ciBib3hwbG90c19leHByX3NlbGVjdGVkX2dlbmVzLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NSwgb3V0LndpZHRoPSI2MCUiLCBmaWcuY2FwPSJCb3ggcGxvdHMgb2Ygc3RhbmRhcmRpc2VkIGV4cHJlc3Npb24gdmFsdWVzIGJlZm9yZSBhbmQgYWZ0ZXIgZ2VuZSBzZWxlY3Rpb24uICJ9CiMjIyMgSGlzdG9ncmFtIG9mIGV4cHJlc3Npb24gYWZ0ZXIgZ2VuZSBzZWxlY3Rpb24gIyMjIwoKcGFyKG1mcm93ID0gYygxLDIpKQoKYm94cGxvdChmYV9leHByX2xvZzIsIAogICAgICAgIGhvcml6b250YWwgPSBUUlVFLAogICAgICAgIHhsYWIgPSAibG9nMihjb3VudHMpIiwgCiAgICAgICAgbGFzID0gMSwgCiAgICAgICAgY29sID0gZmFfbWV0YSRjb2xvciwgCiAgICAgICAgbWFpbiA9ICJCZWZvcmUgZ2VuZSBzZWxlY3Rpb24iKQpib3hwbG90KGZhX2V4cHJfc2VsZWN0ZWQsIAogICAgICAgIGhvcml6b250YWwgPSBUUlVFLAogICAgICAgIHhsYWIgPSAibG9nMihjb3VudHMpIiwgCiAgICAgICAgbGFzID0gMSwgCiAgICAgICAgY29sID0gZmFfbWV0YSRjb2xvciwgCiAgICAgICAgbWFpbiA9ICJBZnRlciBnZW5lIHNlbGVjdGlvbiIpCgpwYXIobWZyb3cgPSBjKDEsMSkpCmBgYAoKCiMjIyBBQ1AKCkRlc3NpbmV6IHVuIHBsb3QgQUNQIGRlcyDDqWNoYW50aWxsb25zIGVuIGxlcyBjb2xvcmFudCBwYXIgY29uZGl0aW9uIGF2YW50IGV0IGFwcsOocyBub3JtYWxpc2F0aW9uLgoKLSBhdmVjIGxlcyBjb21wdGFnZXMgYnJ1dHMgZGUgbGEgbWF0cmljZSBkJ2V4cHJlc3Npb24gaW5pdGlhbGUgKCRmYV9leHByJCkKCmBgYHtyIGFjcF9yYXdfYWxsX2dlbmVzLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04LCBvdXQud2lkdGg9IjYwJSIsIGZpZy5jYXA9IlBDIHBsb3Qgb2YgdGhlIHNhbXBsZXMgZnJvbSB0aGUgcmF3IGV4cHJlc3Npb24gdmFsdWVzIG9mIGFsbCBnZW5lcy4gIn0KIyMgUmF3IGV4cHJlc3Npb24gdmFsdWVzLCBhbGwgZ2VuZXMKbWFfcGNhX3Jhd190dCA8LSBQQ0EodChmYV9leHByX3JhdyksIAogICAgICAgICAgICAgICAgICAgICAgc2NhbGUudW5pdCA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgIGdyYXBoID0gRkFMU0UpCiMgcGxvdChtYV9wY2FfcmF3X3R0LCBjaG9peCA9ICJpbmQiKQpmdml6X3BjYV9pbmQobWFfcGNhX3Jhd190dCwgY29sLmluZCA9IGZhX21ldGFbLCJjb25kaXRpb24iXSkKYGBgCgo8IS0tIC0gYXZlYyBsZXMgY29tcHRhZ2VzIGJydXRzLCBtYWlzIGVuIHJlc3RyZWlnbmFudCBsJ2FuYWx5c2UgYXV4IGfDqG5lcyBzw6lsZWN0aW9ubsOpcyAoJGZhX2V4cHJfcmF3W3NlbGVjdGVkX2dlbmVzLF0kKSAtLT4KCgo8IS0tIGBgYHtyIGFjcF9yYXdfc2VsZWN0ZWRfZ2VuZXMsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTgsIG91dC53aWR0aD0iNjAlIiwgZmlnLmNhcD0iUEMgcGxvdCBvZiB0aGUgc2FtcGxlcyBmcm9tIHRoZSByYXcgZXhwcmVzc2lvbiB2YWx1ZXMgb2Ygc2VsZWN0ZWQgZ2VuZXMuICJ9IC0tPgo8IS0tICMjIFJhdyB2YWx1ZXMgd2l0aCBvbmx5IHRoZSBzZWxlY3RlZCBnZW5lcyAtLT4KPCEtLSBtYV9wY2FfcmF3X3NlbCA8LSBQQ0EodChmYV9leHByX3Jhd1tzZWxlY3RlZF9nZW5lcyxdKSwgIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICBzY2FsZS51bml0ID0gRkFMU0UsICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgZ3JhcGggPSBGQUxTRSkgLS0+CjwhLS0gZnZpel9wY2FfaW5kKG1hX3BjYV9yYXdfc2VsLCBjb2wuaW5kID0gZmFfbWV0YVssICJjb2xvciJdKSAtLT4KPCEtLSBgYGAgLS0+CgoKLSBhdmVjIGxhIG1hdHJpY2UgZGUgdmFsZXVycyBub3JtYWxpc8OpZXMgZGVzIGfDqG5lcyBmaWx0csOpcwoKYGBge3IgYWNwX25vcm1fZmlsdGVyZWRfZ2VuZXMsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTgsIG91dC53aWR0aD0iNjAlIiwgZmlnLmNhcD0iUEMgcGxvdCBvZiB0aGUgc2FtcGxlcyBmcm9tIG5vcm1hbGlzZWQgY291bnRzLCBiZWZvcmUgZ2VuZSBzZWxlY3Rpb24uICJ9Cm1hX3BjYV9maWx0ZXJlZCA8LSBQQ0EodChmYV9leHByX2xvZzIpLCBzY2FsZS51bml0ID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgIGdyYXBoID0gRkFMU0UpCiMgcGxvdChtYV9wY2FfZmlsdGVyZWQsIGNob2l4ID0gInZhciIpCiMgcGxvdChtYV9wY2Ffc2VsLCBjaG9peCA9ICJpbmQiKQpmdml6X3BjYV9pbmQobWFfcGNhX2ZpbHRlcmVkLCBjb2wuaW5kID0gZmFfbWV0YVssICJjb25kaXRpb24iXSkKYGBgCgotIGF2ZWMgbGEgbWF0cmljZSBmaW5hbGUgKHRyYW5zZm9ybWF0aW9uIGxvZzIsIGZpbHRyZSBkZXMgZ8OobmVzIG5vbi1kw6l0ZWN0w6lzLCBzdGFuZGFyZGlzYXRpb24gZXQgc8OpbGVjdGlvbiBkZXMgZ8OobmVzIGZvcnRlbWVudCBleHByaW3DqXMgZXQgw6AgaGF1dCBjb2VmZmljaWVudCBkZSB2YXJpYXRpb24pCgpgYGB7ciBhY3Bfbm9ybV9zZWxlY3RlZF9nZW5lcywgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OCwgb3V0LndpZHRoPSI2MCUiLCBmaWcuY2FwPSJQQyBwbG90IG9mIHRoZSBzYW1wbGVzIGZyb20gbm9ybWFsaXNlZCB2YWx1ZXMsIGFmdGVyIGdlbmUgc2VsZWN0aW9uLiAifQptYV9wY2Ffc2VsIDwtIFBDQSh0KGZhX2V4cHJfc2VsZWN0ZWQpLCBzY2FsZS51bml0ID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgIGdyYXBoID0gRkFMU0UpCiMgcGxvdChtYV9wY2Ffc2VsLCBjaG9peCA9ICJ2YXIiKQojIHBsb3QobWFfcGNhX3NlbCwgY2hvaXggPSAiaW5kIikKZnZpel9wY2FfaW5kKG1hX3BjYV9zZWwsIGNvbC5pbmQgPSBmYV9tZXRhWywgImNvbmRpdGlvbiJdKQpgYGAKCiMjIyBDbHVzdGVyaW5nCgotIENhbGN1bGV6IGxlcyBtYXRyaWNlcyBkZSBkaXN0YW5jZSBlbnRyZSDDqWNoYW50aWxsb25zLCBlbiB1dGlsaXNhbnQgcmVzcGVjdGl2ZW1lbnQgbGVzIGRpc3RhbmNlcyBldWNsaWRpZW5uZSAoYGRpc3QoKWApLCBjb2VmZmljaWVudCBkZSBQZWFyc29uIChgY29yKCwgbWV0aG9kID0gInBlYXJzb24iKWApICBldCBkZSBTcGVhcm1hbiAoYGNvcigsIG1ldGhvZCA9ICJzcGVhcm1hbiIpYCkuCgoKYGBge3Igc2FtcGxlX2Rpc3RhbmNlc30KIyMjIyBTYW1wbGUgZGlzdGFuY2VzICMjIyMKZGlzdF9ldWNfc2FtcGxlcyA8LSBkaXN0KHQoZmFfZXhwcl9zZWxlY3RlZCkpCmNvcl9wZWFyc29uX3NhbXBsZXMgPC0gYXMuZGlzdCgxIC0gY29yKGZhX2V4cHJfc2VsZWN0ZWQpKQpjb3Jfc3BlYXJtYW5fc2FtcGxlcyA8LSBhcy5kaXN0KDEgLSBjb3IoZmFfZXhwcl9zZWxlY3RlZCwgbWV0aG9kID0gInNwZWFybWFuIikpCmBgYAoKLSBFZmZlY3R1ZXogdW4gY2x1c3RlcmluZyBoacOpcmFyY2hpcXVlIGRlcyDDqWNoYW50aWxsb25zLCBlbiB1dGlsaXNhbnQgbGUgY3JpdMOocmUgImNvbXBsZXRlIiBwb3VyIGwnYWdnbG9tw6lyYXRpb24uIENvbXBhcmV6IGxlcyBhcmJyZXMgZCfDqWNoYW50aWxsb25zIG9idGVudXMgYXZlYyBjZXMgdHJvaXMgbcOpdHJpcXVlcyBldCBjaG9pc2lzc2V6IGNlbGxlIHF1aSB2b3VzIHBhcmHDrnQgbGEgcGx1cyBwZXJ0aW5lbnRlLgoKYGBge3Igc2FtcGxlX2NsdXN0ZXJpbmcsIGZpZy53aWR0aD0xMiwgcGxvdC5oZWlnaHQ9NSwgb3V0LndpZHRoPSIxMDAlIiwgZmlnLmNhcD0iU2FtcGxlIHRyZWUgd2l0aCB0aHJlZSBhbHRlcm5hdGl2ZSBkaXN0YW5jZSBtZXRyaWNzOiBFdWNsaWRpYW50IGRpc3RhbmNlIChsZWZ0KSwgUGVhcnNvbiBjb3JyZWxhdGlvbiAoY2VudGVyKSwgU3BlYXJtYW4gY29ycmVsYXRpb24gKHJpZ2h0KS4/ICJ9CiMjIyMgU2FtcGxlIGNsdXN0ZXJpbmcgIyMjIwpwYXIobWZyb3cgPSBjKDEsMykpCnBsb3QoaGNsdXN0KGRpc3RfZXVjX3NhbXBsZXMpLCBoYW5nID0gLTEsCiAgICAgbWFpbiA9ICJldWNsaWRlYW4gZGlzdGFuY2UiKQpwbG90KGhjbHVzdChjb3JfcGVhcnNvbl9zYW1wbGVzKSwgaGFuZyA9IC0xLAogICAgIG1haW4gPSAicGVhcnNvbiIpCnBsb3QoaGNsdXN0KGNvcl9zcGVhcm1hbl9zYW1wbGVzKSwgaGFuZyA9IC0xLAogICAgIG1haW4gPSAic3BlYXJtYW4iKQpwYXIobWZyb3cgPSBjKDEsMSkpCmBgYAoKLSBFZmZlY3R1ZXogdW4gY2x1c3RlcmluZyBoacOpcmFyY2hpcXVlIGRlcyBnw6huZXMgZW4gdXRpbGlzYW50IGxhIGRpc3RhbmNlIGJhc8OpZSBzdXIgbGUgY29lZmZpY2llbnQgZGUgUGVhcnNvbiBldCBsZSBjcml0w6hyZSBkZSBXYXJkCgpgYGB7ciBnZW5lX3RyZWV9CiMjIyMgR2VuZSB0cmVlIHdpdGggUGVhcnNvbiBjb3JyZWxhdGlvbiAjIyMjCmNsdXN0ZXJpbmdfbWV0aG9kIDwtICJjb21wbGV0ZSIgIyMgQ2hvb3NlIGEgY2x1c3RlcmluZyBtZXRob2QKY29yX3BlYXJzb25fZ2VuZXMgPC0gIGFzLmRpc3QoMSAtIGNvcih0KGZhX2V4cHJfc2VsZWN0ZWQpKSkKCiMjIEJ1aWxkIGEgZ2VuZSB0cmVlCmdlbmVfdHJlZV9wZWFyc29uIDwtIGhjbHVzdChjb3JfcGVhcnNvbl9nZW5lcywgbWV0aG9kID0gY2x1c3RlcmluZ19tZXRob2QpCgojIyBQbG90IHRoZSBnZW5lIHRyZWUKcGxvdChnZW5lX3RyZWVfcGVhcnNvbiwgaGFuZyA9IC0xLCBsYXMgPSAxLCAKICAgICBtYWluID0gcGFzdGUwKCJHZW5lIHRyZWU7IFBlYXJzb24gY29lZmZpY2llbnQ7ICIsIGNsdXN0ZXJpbmdfbWV0aG9kLCAiIGNsdXN0ZXJpbmciKSwgCiAgICAgbGFiZWxzID0gRkFMU0UpCmBgYAoKLSBEZXNzaW5leiB1biBhcmJyZSBhdmVjIGxlIHLDqXN1bHRhdCBkdSBjbHVzdGVyaW5nIGRlcyBnw6huZXMgZXQgY29tbWVudGV6IHNhIHN0cnVjdHVyZS4gU2kgdm91cyBkZXZpZXogY2hvaXNpciBkZSBmYcOnb24gYXJiaXRyYWlyZSB1biBub21icmUgZGUgY2x1c3RlcnMsIHF1ZSBjaG9pc2lyaWV6LXZvdXMgPyBQb3VycXVvaSA/IFBhcyBkZSBwYW5pcXVlLCBub3VzIHBvdXZvbnMgYXNzdW1lciBpY2kgcXVlIGxhIHLDqXBvbnNlIGNvbXBvcnRlIHVuZSBwYXJ0IGRlIHN1YmplY3Rpdml0w6kuIAoKYGBge3IgZ2VuZV90cmVlX3dpdGhfYm94ZXN9CiMjIyMgUGxvdCB0aGUgZ2VuZSB0cmVlIHdpdGggYm94ZXMgdG8gZGVub3RlIHRoZSBjbHVzdGVycyAjIyMjCnBsb3QoZ2VuZV90cmVlX3BlYXJzb24sIGhhbmcgPSAtMSwKICAgICBtYWluID0gcGFzdGUwKCJHZW5lIHRyZWUgd2l0aCA3IGNsdXN0ZXJzOyBQZWFyc29uIGNvZWZmaWNpZW50OyAiLCBjbHVzdGVyaW5nX21ldGhvZCwgIiBjbHVzdGVyaW5nIiksIAogICAgIGxhYmVscyA9IEZBTFNFKQpyZWN0LmhjbHVzdChnZW5lX3RyZWVfcGVhcnNvbiwgayA9IDcpCmBgYAoKLSBEZXNzaW5leiB1bmUgaGVhdG1hcCBkdSByw6lzdWx0YXQsIGVuIHPDqWxlY3Rpb25uYW50IGxlcyBkZXV4IHLDqXN1bHRhdHMgZGUgY2x1c3RlcmluZyBjaS1kZXNzdXMgcG91ciBsZXMgZ8OobmVzIGV0IGxlcyDDqWNoYW50aWxsb25zLiAKCmBgYHtyIGhlYXRtYXBfYmljbHVzdGVyaW5nLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OCwgb3V0LndpZHRoPSI5MCUiLCBmaWcuY2FwPSJIZWF0bWFwIHNob3dpbmcgdGhlIGJpY2x1c3RlcmluZyBvZiBnZW5lcyBhbmQgc2FtcGxlcy4gIn0KIyMjIyBIZWF0bWFwICMjIyMKcGhlYXRtYXAodChmYV9leHByX3NlbGVjdGVkKSwgCiAgICAgICAgIG1haW4gPSBwYXN0ZTAoIkJpY2x1c3RlcmluZzsgIiwgIlBlYXJzb24gY29ycmVsYXRpb247ICIsIGNsdXN0ZXJpbmdfbWV0aG9kLCAiIGNsdXN0ZXJpbmciKSwKICAgICAgICAgbGFiZWxzX2NvbCA9ICIiLCAKICAgICAgICAgY2x1c3RlcmluZ19tZXRob2QgPSBjbHVzdGVyaW5nX21ldGhvZCwgCiAgICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJjb3JyZWxhdGlvbiIsIAogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iKQpgYGAKCi0gRGVzc2luZXogdW5lIGhlYXRtYXAgZHUgcsOpc3VsdGF0LCBlbiBhZmZpY2hhbnQgdW4gYXJicmUgc3VyIGxlcyBnw6huZXMgbWFpcyBwYXMgc3VyIGxlcyDDqWNoYW50aWxsb25zCgpgYGB7ciBiaWNsdXN0ZXJpbmd9CnBoZWF0bWFwKHQoZmFfZXhwcl9zZWxlY3RlZCksIAogICAgICAgICBtYWluID0gcGFzdGUwKCJHZW5lIGNsdXN0ZXJpbmc7ICIsICJQZWFyc29uIGNvcnJlbGF0aW9uOyAiLCBjbHVzdGVyaW5nX21ldGhvZCwgIiBjbHVzdGVyaW5nIiksCiAgICAgICAgIGxhYmVsc19jb2wgPSAiIiwgCiAgICAgICAgIGNsdXN0ZXJpbmdfbWV0aG9kID0gY2x1c3RlcmluZ19tZXRob2QsIAogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiY29ycmVsYXRpb24iLCAKICAgICAgICAgY2x1c3Rlcl9yb3dzID0gRkFMU0UpCmBgYAoKCkludGVycHLDqXRleiBsZXMgcsOpc3VsdGF0cyBlbiBxdWVscXVlcyBwaHJhc2VzLiAKCgojIyA1LiBFbnJpY2hpc3NlbWVudCBmb25jdGlvbm5lbAoKKioqQSB2b3VzIGRlIGpvdWVyISoqKgoKRWZmZWN0dWV6IHVuZSBhbmFseXNlIGQnZW5yaWNoaXNzZW1lbnQgZm9uY3Rpb25uZWwgYXZlYyBsZXMgcHJpbmNpcGF1eCBjbHVzdGVycyBvYnRlbnVzIGRhbnMgbGEgc2VjdGlvbiBwcsOpY8OpZGVudGUuIAoKYGBge3IgZnVuY3Rpb25hbF9lbnJpY2htZW50fQojIyMjIFJ1biBlbnJpY2htZW50IGFuYWx5c2lzIHdpdGggZ29zdCgpICMjIyMKbWVzc2FnZSgiUnVubmluZyBlbnJpY2htZW50IGFuYWx5c2lzIHdpdGggZ29zdCIpCgojIyBHZXQgdGhlIGxpc3Qgb2YgSURzIGZvciB0aGUgc2VsZWN0ZWQgZ2VuZXMgIyMKZ2VuZV9pZHMgPC0gcm93bmFtZXMoZ2VuZV9zdGF0X25vcm0pW3NlbGVjdGVkX2dlbmVzXQoKIyMgR2V0IHRoZSBsaXN0IG9mIGdlbmUgbmFtZXMgZm9yIHRoZSBzZWxlY3RlZCBnZW5lcyAjIwojIGdlbmVfbmFtZXMgPC0gdW5saXN0KHN1YnNldCh4ID0gZ2VuZV9zdGF0X25vcm0sIAojICAgICAgICAgICAgICAgICAgICAgc3Vic2V0ID0gc2VsZWN0ZWRfZ2VuZXMsCiMgICAgICAgICAgICAgICAgICAgICBzZWxlY3QgPSBleHRlcm5hbF9nZW5lX25hbWUpKQojIGxlbmd0aChnZW5lX25hbWVzKQoKIyMKCmdvc3RyZXMgPC0gZ29zdChxdWVyeSA9IGdlbmVfaWRzLCAKICAgICAgICAgICAgICAgIG9yZ2FuaXNtID0gIm1tdXNjdWx1cyIsIAogICAgICAgICAgICAgICAgb3JkZXJlZF9xdWVyeSA9IEZBTFNFLCAKICAgICAgICAgICAgICAgIG11bHRpX3F1ZXJ5ID0gRkFMU0UsIAogICAgICAgICAgICAgICAgc2lnbmlmaWNhbnQgPSBUUlVFLCAKICAgICAgICAgICAgICAgIGV4Y2x1ZGVfaWVhID0gRkFMU0UsIAogICAgICAgICAgICAgICAgbWVhc3VyZV91bmRlcnJlcHJlc2VudGF0aW9uID0gRkFMU0UsIAogICAgICAgICAgICAgICAgZXZjb2RlcyA9IEZBTFNFLCAKICAgICAgICAgICAgICAgIHVzZXJfdGhyZXNob2xkID0gMC4wNSwgCiAgICAgICAgICAgICAgICBjb3JyZWN0aW9uX21ldGhvZCA9ICJmZHIiLCAKICAgICAgICAgICAgICAgIGRvbWFpbl9zY29wZSA9ICJhbm5vdGF0ZWQiLCAKICAgICAgICAgICAgICAgIGN1c3RvbV9iZyA9IE5VTEwsIAogICAgICAgICAgICAgICAgbnVtZXJpY19ucyA9ICIiLCAKICAgICAgICAgICAgICAgIHNvdXJjZXMgPSBOVUxMLCAKICAgICAgICAgICAgICAgIGFzX3Nob3J0X2xpbmsgPSBGQUxTRSkKCgojIyBDaGVjayB0aGUgc3RydWN0dXJlIG9mIHRoZSByZXN1bHQKbmFtZXMoZ29zdHJlcykKbmFtZXMoZ29zdHJlcyRyZXN1bHQpCmthYmxlKHN1bW1hcnkoZ29zdHJlcykpCgojIFZpc3VhbGl6YXRpb24KZ29zdHBsb3QoZ29zdHJlcywgY2FwcGVkID0gVFJVRSwgaW50ZXJhY3RpdmUgPSBUUlVFKQoKIyBoaXN0KGdvc3RyZXMkcmVzdWx0JHBfdmFsdWUsIGJyZWFrcz1zZXEoZnJvbT0wLCB0bz0xLCBieSA9IDAuMDUpKQoKIyMgQ2hlY2sgdGhlIG1vc3Qgc2lnbmlmaWNhbnQgcmVzdWx0cywgZm9ybWF0aW5nIGZvciBrYWJsZQojaGVhZChnb3N0cmVzJHJlc3VsdCkKZW5yaWNoX29yZGVyIDwtIG9yZGVyKGdvc3RyZXMkcmVzdWx0JHBfdmFsdWUsIGRlY3JlYXNpbmcgPSBGQUxTRSkKc29ydGVkX3Jlc3VsdCA8LSBnb3N0cmVzJHJlc3VsdFtlbnJpY2hfb3JkZXIsIF0Ka2FibGUoaGVhZChzb3J0ZWRfcmVzdWx0LCBuID0gMTApLCAKICAgICAgZGlnaXRzID0gYygwLCAwLCAxNSwgNywgNywgNywgMywgMywgMCwgMCwgNywgNywgMCwgMCksIAogICAgICBjYXB0aW9uID0gIlRvcCAxMCBtb3N0IHNpZ25pZmljYW50IGVucmljaGVkIGZ1bmN0aW9uYWwgY2xhc3NlcyIpCiMga2FibGUoaGVhZChnb3N0cmVzJHJlc3VsdCksIAojICAgICAgIGZvcm1hdCA9IGMoIiVzIiwgIiVzIiwgIiVlIiwgIiVkIiwgIiVkIiwgIiVkIiwgIiUuM2YiLCAiJS4zZiIsICIlcyIsICIlcyIsICIlZCIsICIlZCIsICIlcyIsICIlcyIpKQpuYW1lcyhnb3N0cmVzJG1ldGEpCmBgYAoKIyMgQ29uY2x1c2lvbnMgZ8OpbsOpcmFsZXMKCgpSw6lzdW1leiBlbiBxdWVscXVlcyBwaHJhc2VzIHZvcyBjb25jbHVzaW9ucyDDoCBwYXJ0aXIgZGVzIHLDqXN1bHRhdHMgb2J0ZW51cy4gCgoKIyMgU2Vzc2lvbiBpbmZvCgoKYGBge3Igc2Vzc2lvbl9pbmZvfQojIyMjIFNlc3Npb24gaW5mbyAjIyMjCnNlc3Npb25JbmZvKCkKCmBgYAoK